wordspace 0.0.2 → 0.0.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.
- package/dist/commands/init.d.ts +1 -0
- package/dist/commands/init.js +41 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -1
- package/dist/lib/exec.d.ts +6 -0
- package/dist/lib/exec.js +21 -0
- package/dist/lib/log.d.ts +7 -0
- package/dist/lib/log.js +30 -0
- package/dist/steps/check-skills.d.ts +3 -0
- package/dist/steps/check-skills.js +8 -0
- package/dist/steps/create-dirs.d.ts +1 -0
- package/dist/steps/create-dirs.js +7 -0
- package/dist/steps/create-symlinks.d.ts +1 -0
- package/dist/steps/create-symlinks.js +30 -0
- package/dist/steps/fetch-workflows.d.ts +1 -0
- package/dist/steps/fetch-workflows.js +78 -0
- package/dist/steps/install-skills.d.ts +1 -0
- package/dist/steps/install-skills.js +10 -0
- package/dist/steps/setup-claude.d.ts +1 -0
- package/dist/steps/setup-claude.js +47 -0
- package/package.json +4 -1
- package/src/commands/init.ts +0 -48
- package/src/index.ts +0 -51
- package/src/lib/exec.ts +0 -30
- package/src/lib/log.ts +0 -38
- package/src/steps/check-skills.ts +0 -11
- package/src/steps/create-dirs.ts +0 -8
- package/src/steps/create-symlinks.ts +0 -39
- package/src/steps/fetch-workflows.ts +0 -92
- package/src/steps/install-skills.ts +0 -11
- package/src/steps/setup-claude.ts +0 -61
- package/tsconfig.json +0 -18
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function init(force: boolean): Promise<void>;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { checkSkills } from "../steps/check-skills.js";
|
|
2
|
+
import { installSkills } from "../steps/install-skills.js";
|
|
3
|
+
import { fetchWorkflows } from "../steps/fetch-workflows.js";
|
|
4
|
+
import { createSymlinks } from "../steps/create-symlinks.js";
|
|
5
|
+
import { setupClaude } from "../steps/setup-claude.js";
|
|
6
|
+
import { createDirs } from "../steps/create-dirs.js";
|
|
7
|
+
import * as log from "../lib/log.js";
|
|
8
|
+
export async function init(force) {
|
|
9
|
+
const cwd = process.cwd();
|
|
10
|
+
log.banner();
|
|
11
|
+
// Step 1: Check skills
|
|
12
|
+
log.step("1/6 Skills");
|
|
13
|
+
const hasSkills = checkSkills(cwd);
|
|
14
|
+
if (hasSkills && !force) {
|
|
15
|
+
log.skip("All skills already installed");
|
|
16
|
+
}
|
|
17
|
+
else {
|
|
18
|
+
installSkills(cwd);
|
|
19
|
+
}
|
|
20
|
+
// Step 2: Fetch workflows
|
|
21
|
+
log.step("2/6 Workflows");
|
|
22
|
+
await fetchWorkflows(cwd, force);
|
|
23
|
+
// Step 3: Create symlinks
|
|
24
|
+
log.step("3/6 Symlinks");
|
|
25
|
+
createSymlinks(cwd, force);
|
|
26
|
+
// Step 4: Setup Claude settings
|
|
27
|
+
log.step("4/6 Claude settings");
|
|
28
|
+
setupClaude(cwd);
|
|
29
|
+
// Step 5: Create directories
|
|
30
|
+
log.step("5/6 Directories");
|
|
31
|
+
createDirs(cwd);
|
|
32
|
+
// Step 6: Done
|
|
33
|
+
log.step("6/6 Done");
|
|
34
|
+
console.log(`
|
|
35
|
+
Your project is ready. Next steps:
|
|
36
|
+
|
|
37
|
+
1. Open this directory in your editor
|
|
38
|
+
2. Start Claude Code: claude
|
|
39
|
+
3. Run a workflow: prose run workflows/<name>.prose
|
|
40
|
+
`);
|
|
41
|
+
}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
CHANGED
package/dist/lib/exec.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { execSync } from "node:child_process";
|
|
2
|
+
import * as log from "./log.js";
|
|
3
|
+
export function exec(cmd, opts = {}) {
|
|
4
|
+
const { cwd = process.cwd(), timeout = 60_000, silent = false } = opts;
|
|
5
|
+
try {
|
|
6
|
+
const result = execSync(cmd, {
|
|
7
|
+
cwd,
|
|
8
|
+
timeout,
|
|
9
|
+
stdio: silent ? "pipe" : ["pipe", "pipe", "pipe"],
|
|
10
|
+
encoding: "utf-8",
|
|
11
|
+
});
|
|
12
|
+
return result.trim();
|
|
13
|
+
}
|
|
14
|
+
catch (err) {
|
|
15
|
+
const e = err;
|
|
16
|
+
const msg = e.stderr?.trim() || e.message || "Command failed";
|
|
17
|
+
log.error(`Command failed: ${cmd}`);
|
|
18
|
+
log.error(msg);
|
|
19
|
+
throw new Error(`exec failed: ${cmd}`);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare function info(msg: string): void;
|
|
2
|
+
export declare function success(msg: string): void;
|
|
3
|
+
export declare function warn(msg: string): void;
|
|
4
|
+
export declare function error(msg: string): void;
|
|
5
|
+
export declare function step(msg: string): void;
|
|
6
|
+
export declare function skip(msg: string): void;
|
|
7
|
+
export declare function banner(): void;
|
package/dist/lib/log.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
const noColor = !!process.env["NO_COLOR"];
|
|
2
|
+
const code = (n) => (noColor ? "" : `\x1b[${n}m`);
|
|
3
|
+
const reset = code(0);
|
|
4
|
+
const bold = code(1);
|
|
5
|
+
const dim = code(2);
|
|
6
|
+
const green = code(32);
|
|
7
|
+
const yellow = code(33);
|
|
8
|
+
const red = code(31);
|
|
9
|
+
const cyan = code(36);
|
|
10
|
+
export function info(msg) {
|
|
11
|
+
console.log(`${cyan}i${reset} ${msg}`);
|
|
12
|
+
}
|
|
13
|
+
export function success(msg) {
|
|
14
|
+
console.log(`${green}✓${reset} ${msg}`);
|
|
15
|
+
}
|
|
16
|
+
export function warn(msg) {
|
|
17
|
+
console.log(`${yellow}!${reset} ${msg}`);
|
|
18
|
+
}
|
|
19
|
+
export function error(msg) {
|
|
20
|
+
console.error(`${red}✗${reset} ${msg}`);
|
|
21
|
+
}
|
|
22
|
+
export function step(msg) {
|
|
23
|
+
console.log(`\n${bold}${msg}${reset}`);
|
|
24
|
+
}
|
|
25
|
+
export function skip(msg) {
|
|
26
|
+
console.log(`${dim}–${reset} ${dim}${msg}${reset}`);
|
|
27
|
+
}
|
|
28
|
+
export function banner() {
|
|
29
|
+
console.log(`\n${bold}wordspace init${reset}\n`);
|
|
30
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
const SKILLS = ["open-prose", "agentwallet", "registry", "websh"];
|
|
4
|
+
export function checkSkills(cwd) {
|
|
5
|
+
const base = join(cwd, ".agents", "skills");
|
|
6
|
+
return SKILLS.every((s) => existsSync(join(base, s)));
|
|
7
|
+
}
|
|
8
|
+
export { SKILLS };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function createDirs(cwd: string): void;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function createSymlinks(cwd: string, force: boolean): void;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { mkdirSync, symlinkSync, readlinkSync, lstatSync, unlinkSync, } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { SKILLS } from "./check-skills.js";
|
|
4
|
+
import * as log from "../lib/log.js";
|
|
5
|
+
export function createSymlinks(cwd, force) {
|
|
6
|
+
const skillsDir = join(cwd, "skills");
|
|
7
|
+
mkdirSync(skillsDir, { recursive: true });
|
|
8
|
+
for (const skill of SKILLS) {
|
|
9
|
+
const linkPath = join(skillsDir, skill);
|
|
10
|
+
const target = join("..", ".agents", "skills", skill);
|
|
11
|
+
// Check if symlink already exists and points to the right target
|
|
12
|
+
try {
|
|
13
|
+
const stat = lstatSync(linkPath);
|
|
14
|
+
if (stat.isSymbolicLink()) {
|
|
15
|
+
const existing = readlinkSync(linkPath);
|
|
16
|
+
if (existing === target && !force) {
|
|
17
|
+
log.skip(`skills/${skill} (exists)`);
|
|
18
|
+
continue;
|
|
19
|
+
}
|
|
20
|
+
// Remove stale or forced symlink
|
|
21
|
+
unlinkSync(linkPath);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
// Does not exist — will create
|
|
26
|
+
}
|
|
27
|
+
symlinkSync(target, linkPath);
|
|
28
|
+
log.success(`skills/${skill} -> ${target}`);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function fetchWorkflows(cwd: string, force: boolean): Promise<void>;
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { get as httpsGet } from "node:https";
|
|
2
|
+
import { mkdirSync, writeFileSync, existsSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import * as log from "../lib/log.js";
|
|
5
|
+
const CONTENTS_URL = "https://api.github.com/repos/frames-engineering/wordspace-demos/contents/workflows";
|
|
6
|
+
function httpGet(url, headers = {}) {
|
|
7
|
+
return new Promise((resolve, reject) => {
|
|
8
|
+
const allHeaders = {
|
|
9
|
+
"User-Agent": "wordspace-cli",
|
|
10
|
+
...headers,
|
|
11
|
+
};
|
|
12
|
+
httpsGet(url, { headers: allHeaders }, (res) => {
|
|
13
|
+
// Follow redirects
|
|
14
|
+
if ((res.statusCode === 301 || res.statusCode === 302) && res.headers.location) {
|
|
15
|
+
httpGet(res.headers.location, headers).then(resolve, reject);
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
if (res.statusCode !== 200) {
|
|
19
|
+
reject(new Error(`HTTP ${res.statusCode} for ${url}`));
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
let data = "";
|
|
23
|
+
res.on("data", (chunk) => (data += chunk.toString()));
|
|
24
|
+
res.on("end", () => resolve(data));
|
|
25
|
+
res.on("error", reject);
|
|
26
|
+
}).on("error", reject);
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
function getAuthHeaders() {
|
|
30
|
+
const token = process.env["GITHUB_TOKEN"] || process.env["GH_TOKEN"];
|
|
31
|
+
if (token) {
|
|
32
|
+
return { Authorization: `Bearer ${token}` };
|
|
33
|
+
}
|
|
34
|
+
return {};
|
|
35
|
+
}
|
|
36
|
+
export async function fetchWorkflows(cwd, force) {
|
|
37
|
+
const workflowsDir = join(cwd, "workflows");
|
|
38
|
+
mkdirSync(workflowsDir, { recursive: true });
|
|
39
|
+
const headers = getAuthHeaders();
|
|
40
|
+
let entries;
|
|
41
|
+
try {
|
|
42
|
+
const body = await httpGet(CONTENTS_URL, headers);
|
|
43
|
+
entries = JSON.parse(body);
|
|
44
|
+
}
|
|
45
|
+
catch (err) {
|
|
46
|
+
log.warn(`Could not fetch workflow list from GitHub: ${err.message}`);
|
|
47
|
+
log.warn("Skipping workflow download (skills are the critical part).");
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
const proseFiles = entries.filter((e) => e.name.endsWith(".prose"));
|
|
51
|
+
if (proseFiles.length === 0) {
|
|
52
|
+
log.warn("No .prose files found in workflows/");
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
let downloaded = 0;
|
|
56
|
+
for (const file of proseFiles) {
|
|
57
|
+
const dest = join(workflowsDir, file.name);
|
|
58
|
+
if (existsSync(dest) && !force) {
|
|
59
|
+
log.skip(`${file.name} (exists)`);
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
try {
|
|
63
|
+
const content = await httpGet(file.download_url, headers);
|
|
64
|
+
writeFileSync(dest, content, "utf-8");
|
|
65
|
+
log.success(file.name);
|
|
66
|
+
downloaded++;
|
|
67
|
+
}
|
|
68
|
+
catch (err) {
|
|
69
|
+
log.warn(`Failed to download ${file.name}: ${err.message}`);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
if (downloaded > 0) {
|
|
73
|
+
log.success(`Downloaded ${downloaded} workflow(s) to workflows/`);
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
log.skip("All workflows already present");
|
|
77
|
+
}
|
|
78
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function installSkills(cwd: string): void;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { exec } from "../lib/exec.js";
|
|
2
|
+
import * as log from "../lib/log.js";
|
|
3
|
+
export function installSkills(cwd) {
|
|
4
|
+
log.info("Installing skills via npx skills add...");
|
|
5
|
+
exec("npx -y skills add frames-engineering/skills -y", {
|
|
6
|
+
cwd,
|
|
7
|
+
timeout: 120_000,
|
|
8
|
+
});
|
|
9
|
+
log.success("Skills installed to .agents/skills/");
|
|
10
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function setupClaude(cwd: string): void;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { mkdirSync, readFileSync, writeFileSync, existsSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import * as log from "../lib/log.js";
|
|
4
|
+
const BASE_PERMISSIONS = [
|
|
5
|
+
"Bash(curl:*)",
|
|
6
|
+
"Bash(python3:*)",
|
|
7
|
+
"WebFetch(domain:registry.mcpay.tech)",
|
|
8
|
+
"WebFetch(domain:frames.ag)",
|
|
9
|
+
"WebSearch",
|
|
10
|
+
];
|
|
11
|
+
export function setupClaude(cwd) {
|
|
12
|
+
const claudeDir = join(cwd, ".claude");
|
|
13
|
+
mkdirSync(claudeDir, { recursive: true });
|
|
14
|
+
const settingsPath = join(claudeDir, "settings.local.json");
|
|
15
|
+
let settings = {};
|
|
16
|
+
if (existsSync(settingsPath)) {
|
|
17
|
+
try {
|
|
18
|
+
settings = JSON.parse(readFileSync(settingsPath, "utf-8"));
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
log.warn("Could not parse existing settings.local.json, creating fresh");
|
|
22
|
+
settings = {};
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
if (!settings.permissions) {
|
|
26
|
+
settings.permissions = {};
|
|
27
|
+
}
|
|
28
|
+
if (!Array.isArray(settings.permissions.allow)) {
|
|
29
|
+
settings.permissions.allow = [];
|
|
30
|
+
}
|
|
31
|
+
// Merge base permissions (deduplicate)
|
|
32
|
+
const existing = new Set(settings.permissions.allow);
|
|
33
|
+
let added = 0;
|
|
34
|
+
for (const perm of BASE_PERMISSIONS) {
|
|
35
|
+
if (!existing.has(perm)) {
|
|
36
|
+
settings.permissions.allow.push(perm);
|
|
37
|
+
added++;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n", "utf-8");
|
|
41
|
+
if (added > 0) {
|
|
42
|
+
log.success(`Added ${added} permission(s) to .claude/settings.local.json`);
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
log.skip("All base permissions already present");
|
|
46
|
+
}
|
|
47
|
+
}
|
package/package.json
CHANGED
package/src/commands/init.ts
DELETED
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
import { checkSkills } from "../steps/check-skills.js";
|
|
2
|
-
import { installSkills } from "../steps/install-skills.js";
|
|
3
|
-
import { fetchWorkflows } from "../steps/fetch-workflows.js";
|
|
4
|
-
import { createSymlinks } from "../steps/create-symlinks.js";
|
|
5
|
-
import { setupClaude } from "../steps/setup-claude.js";
|
|
6
|
-
import { createDirs } from "../steps/create-dirs.js";
|
|
7
|
-
import * as log from "../lib/log.js";
|
|
8
|
-
|
|
9
|
-
export async function init(force: boolean) {
|
|
10
|
-
const cwd = process.cwd();
|
|
11
|
-
|
|
12
|
-
log.banner();
|
|
13
|
-
|
|
14
|
-
// Step 1: Check skills
|
|
15
|
-
log.step("1/6 Skills");
|
|
16
|
-
const hasSkills = checkSkills(cwd);
|
|
17
|
-
if (hasSkills && !force) {
|
|
18
|
-
log.skip("All skills already installed");
|
|
19
|
-
} else {
|
|
20
|
-
installSkills(cwd);
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
// Step 2: Fetch workflows
|
|
24
|
-
log.step("2/6 Workflows");
|
|
25
|
-
await fetchWorkflows(cwd, force);
|
|
26
|
-
|
|
27
|
-
// Step 3: Create symlinks
|
|
28
|
-
log.step("3/6 Symlinks");
|
|
29
|
-
createSymlinks(cwd, force);
|
|
30
|
-
|
|
31
|
-
// Step 4: Setup Claude settings
|
|
32
|
-
log.step("4/6 Claude settings");
|
|
33
|
-
setupClaude(cwd);
|
|
34
|
-
|
|
35
|
-
// Step 5: Create directories
|
|
36
|
-
log.step("5/6 Directories");
|
|
37
|
-
createDirs(cwd);
|
|
38
|
-
|
|
39
|
-
// Step 6: Done
|
|
40
|
-
log.step("6/6 Done");
|
|
41
|
-
console.log(`
|
|
42
|
-
Your project is ready. Next steps:
|
|
43
|
-
|
|
44
|
-
1. Open this directory in your editor
|
|
45
|
-
2. Start Claude Code: claude
|
|
46
|
-
3. Run a workflow: prose run workflows/<name>.prose
|
|
47
|
-
`);
|
|
48
|
-
}
|
package/src/index.ts
DELETED
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
import { init } from "./commands/init.js";
|
|
4
|
-
import * as log from "./lib/log.js";
|
|
5
|
-
|
|
6
|
-
const VERSION = "0.1.0";
|
|
7
|
-
|
|
8
|
-
const HELP = `
|
|
9
|
-
Usage: wordspace <command> [options]
|
|
10
|
-
|
|
11
|
-
Commands:
|
|
12
|
-
init Bootstrap a new wordspace project
|
|
13
|
-
|
|
14
|
-
Options:
|
|
15
|
-
--force Re-run all steps even if already completed
|
|
16
|
-
--help Show this help message
|
|
17
|
-
--version Show version number
|
|
18
|
-
`.trim();
|
|
19
|
-
|
|
20
|
-
async function main() {
|
|
21
|
-
const args = process.argv.slice(2);
|
|
22
|
-
|
|
23
|
-
if (args.includes("--help") || args.includes("-h")) {
|
|
24
|
-
console.log(HELP);
|
|
25
|
-
process.exit(0);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
if (args.includes("--version") || args.includes("-v")) {
|
|
29
|
-
console.log(VERSION);
|
|
30
|
-
process.exit(0);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
const command = args.find((a) => !a.startsWith("-"));
|
|
34
|
-
const force = args.includes("--force");
|
|
35
|
-
|
|
36
|
-
if (command === "init") {
|
|
37
|
-
await init(force);
|
|
38
|
-
} else if (!command) {
|
|
39
|
-
console.log(HELP);
|
|
40
|
-
process.exit(0);
|
|
41
|
-
} else {
|
|
42
|
-
log.error(`Unknown command: ${command}`);
|
|
43
|
-
console.log(HELP);
|
|
44
|
-
process.exit(1);
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
main().catch((err: Error) => {
|
|
49
|
-
log.error(err.message);
|
|
50
|
-
process.exit(1);
|
|
51
|
-
});
|
package/src/lib/exec.ts
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import { execSync } from "node:child_process";
|
|
2
|
-
import * as log from "./log.js";
|
|
3
|
-
|
|
4
|
-
export interface ExecOptions {
|
|
5
|
-
cwd?: string;
|
|
6
|
-
timeout?: number;
|
|
7
|
-
silent?: boolean;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export function exec(
|
|
11
|
-
cmd: string,
|
|
12
|
-
opts: ExecOptions = {},
|
|
13
|
-
): string {
|
|
14
|
-
const { cwd = process.cwd(), timeout = 60_000, silent = false } = opts;
|
|
15
|
-
try {
|
|
16
|
-
const result = execSync(cmd, {
|
|
17
|
-
cwd,
|
|
18
|
-
timeout,
|
|
19
|
-
stdio: silent ? "pipe" : ["pipe", "pipe", "pipe"],
|
|
20
|
-
encoding: "utf-8",
|
|
21
|
-
});
|
|
22
|
-
return result.trim();
|
|
23
|
-
} catch (err: unknown) {
|
|
24
|
-
const e = err as { stderr?: string; message?: string };
|
|
25
|
-
const msg = e.stderr?.trim() || e.message || "Command failed";
|
|
26
|
-
log.error(`Command failed: ${cmd}`);
|
|
27
|
-
log.error(msg);
|
|
28
|
-
throw new Error(`exec failed: ${cmd}`);
|
|
29
|
-
}
|
|
30
|
-
}
|
package/src/lib/log.ts
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
const noColor = !!process.env["NO_COLOR"];
|
|
2
|
-
|
|
3
|
-
const code = (n: number) => (noColor ? "" : `\x1b[${n}m`);
|
|
4
|
-
const reset = code(0);
|
|
5
|
-
const bold = code(1);
|
|
6
|
-
const dim = code(2);
|
|
7
|
-
const green = code(32);
|
|
8
|
-
const yellow = code(33);
|
|
9
|
-
const red = code(31);
|
|
10
|
-
const cyan = code(36);
|
|
11
|
-
|
|
12
|
-
export function info(msg: string) {
|
|
13
|
-
console.log(`${cyan}i${reset} ${msg}`);
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export function success(msg: string) {
|
|
17
|
-
console.log(`${green}✓${reset} ${msg}`);
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export function warn(msg: string) {
|
|
21
|
-
console.log(`${yellow}!${reset} ${msg}`);
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export function error(msg: string) {
|
|
25
|
-
console.error(`${red}✗${reset} ${msg}`);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export function step(msg: string) {
|
|
29
|
-
console.log(`\n${bold}${msg}${reset}`);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export function skip(msg: string) {
|
|
33
|
-
console.log(`${dim}–${reset} ${dim}${msg}${reset}`);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export function banner() {
|
|
37
|
-
console.log(`\n${bold}wordspace init${reset}\n`);
|
|
38
|
-
}
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { existsSync } from "node:fs";
|
|
2
|
-
import { join } from "node:path";
|
|
3
|
-
|
|
4
|
-
const SKILLS = ["open-prose", "agentwallet", "registry", "websh"] as const;
|
|
5
|
-
|
|
6
|
-
export function checkSkills(cwd: string): boolean {
|
|
7
|
-
const base = join(cwd, ".agents", "skills");
|
|
8
|
-
return SKILLS.every((s) => existsSync(join(base, s)));
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export { SKILLS };
|
package/src/steps/create-dirs.ts
DELETED
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
mkdirSync,
|
|
3
|
-
symlinkSync,
|
|
4
|
-
readlinkSync,
|
|
5
|
-
lstatSync,
|
|
6
|
-
unlinkSync,
|
|
7
|
-
} from "node:fs";
|
|
8
|
-
import { join } from "node:path";
|
|
9
|
-
import { SKILLS } from "./check-skills.js";
|
|
10
|
-
import * as log from "../lib/log.js";
|
|
11
|
-
|
|
12
|
-
export function createSymlinks(cwd: string, force: boolean) {
|
|
13
|
-
const skillsDir = join(cwd, "skills");
|
|
14
|
-
mkdirSync(skillsDir, { recursive: true });
|
|
15
|
-
|
|
16
|
-
for (const skill of SKILLS) {
|
|
17
|
-
const linkPath = join(skillsDir, skill);
|
|
18
|
-
const target = join("..", ".agents", "skills", skill);
|
|
19
|
-
|
|
20
|
-
// Check if symlink already exists and points to the right target
|
|
21
|
-
try {
|
|
22
|
-
const stat = lstatSync(linkPath);
|
|
23
|
-
if (stat.isSymbolicLink()) {
|
|
24
|
-
const existing = readlinkSync(linkPath);
|
|
25
|
-
if (existing === target && !force) {
|
|
26
|
-
log.skip(`skills/${skill} (exists)`);
|
|
27
|
-
continue;
|
|
28
|
-
}
|
|
29
|
-
// Remove stale or forced symlink
|
|
30
|
-
unlinkSync(linkPath);
|
|
31
|
-
}
|
|
32
|
-
} catch {
|
|
33
|
-
// Does not exist — will create
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
symlinkSync(target, linkPath);
|
|
37
|
-
log.success(`skills/${skill} -> ${target}`);
|
|
38
|
-
}
|
|
39
|
-
}
|
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
import { get as httpsGet } from "node:https";
|
|
2
|
-
import { mkdirSync, writeFileSync, existsSync } from "node:fs";
|
|
3
|
-
import { join } from "node:path";
|
|
4
|
-
import * as log from "../lib/log.js";
|
|
5
|
-
|
|
6
|
-
const CONTENTS_URL =
|
|
7
|
-
"https://api.github.com/repos/frames-engineering/wordspace-demos/contents/workflows";
|
|
8
|
-
|
|
9
|
-
interface GitHubEntry {
|
|
10
|
-
name: string;
|
|
11
|
-
download_url: string;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
function httpGet(url: string, headers: Record<string, string> = {}): Promise<string> {
|
|
15
|
-
return new Promise((resolve, reject) => {
|
|
16
|
-
const allHeaders: Record<string, string> = {
|
|
17
|
-
"User-Agent": "wordspace-cli",
|
|
18
|
-
...headers,
|
|
19
|
-
};
|
|
20
|
-
httpsGet(url, { headers: allHeaders }, (res) => {
|
|
21
|
-
// Follow redirects
|
|
22
|
-
if ((res.statusCode === 301 || res.statusCode === 302) && res.headers.location) {
|
|
23
|
-
httpGet(res.headers.location, headers).then(resolve, reject);
|
|
24
|
-
return;
|
|
25
|
-
}
|
|
26
|
-
if (res.statusCode !== 200) {
|
|
27
|
-
reject(new Error(`HTTP ${res.statusCode} for ${url}`));
|
|
28
|
-
return;
|
|
29
|
-
}
|
|
30
|
-
let data = "";
|
|
31
|
-
res.on("data", (chunk: Buffer) => (data += chunk.toString()));
|
|
32
|
-
res.on("end", () => resolve(data));
|
|
33
|
-
res.on("error", reject);
|
|
34
|
-
}).on("error", reject);
|
|
35
|
-
});
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
function getAuthHeaders(): Record<string, string> {
|
|
39
|
-
const token = process.env["GITHUB_TOKEN"] || process.env["GH_TOKEN"];
|
|
40
|
-
if (token) {
|
|
41
|
-
return { Authorization: `Bearer ${token}` };
|
|
42
|
-
}
|
|
43
|
-
return {};
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
export async function fetchWorkflows(cwd: string, force: boolean) {
|
|
47
|
-
const workflowsDir = join(cwd, "workflows");
|
|
48
|
-
mkdirSync(workflowsDir, { recursive: true });
|
|
49
|
-
|
|
50
|
-
const headers = getAuthHeaders();
|
|
51
|
-
|
|
52
|
-
let entries: GitHubEntry[];
|
|
53
|
-
try {
|
|
54
|
-
const body = await httpGet(CONTENTS_URL, headers);
|
|
55
|
-
entries = JSON.parse(body) as GitHubEntry[];
|
|
56
|
-
} catch (err) {
|
|
57
|
-
log.warn(
|
|
58
|
-
`Could not fetch workflow list from GitHub: ${(err as Error).message}`,
|
|
59
|
-
);
|
|
60
|
-
log.warn("Skipping workflow download (skills are the critical part).");
|
|
61
|
-
return;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
const proseFiles = entries.filter((e) => e.name.endsWith(".prose"));
|
|
65
|
-
if (proseFiles.length === 0) {
|
|
66
|
-
log.warn("No .prose files found in workflows/");
|
|
67
|
-
return;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
let downloaded = 0;
|
|
71
|
-
for (const file of proseFiles) {
|
|
72
|
-
const dest = join(workflowsDir, file.name);
|
|
73
|
-
if (existsSync(dest) && !force) {
|
|
74
|
-
log.skip(`${file.name} (exists)`);
|
|
75
|
-
continue;
|
|
76
|
-
}
|
|
77
|
-
try {
|
|
78
|
-
const content = await httpGet(file.download_url, headers);
|
|
79
|
-
writeFileSync(dest, content, "utf-8");
|
|
80
|
-
log.success(file.name);
|
|
81
|
-
downloaded++;
|
|
82
|
-
} catch (err) {
|
|
83
|
-
log.warn(`Failed to download ${file.name}: ${(err as Error).message}`);
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
if (downloaded > 0) {
|
|
88
|
-
log.success(`Downloaded ${downloaded} workflow(s) to workflows/`);
|
|
89
|
-
} else {
|
|
90
|
-
log.skip("All workflows already present");
|
|
91
|
-
}
|
|
92
|
-
}
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { exec } from "../lib/exec.js";
|
|
2
|
-
import * as log from "../lib/log.js";
|
|
3
|
-
|
|
4
|
-
export function installSkills(cwd: string) {
|
|
5
|
-
log.info("Installing skills via npx skills add...");
|
|
6
|
-
exec("npx -y skills add frames-engineering/skills -y", {
|
|
7
|
-
cwd,
|
|
8
|
-
timeout: 120_000,
|
|
9
|
-
});
|
|
10
|
-
log.success("Skills installed to .agents/skills/");
|
|
11
|
-
}
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import { mkdirSync, readFileSync, writeFileSync, existsSync } from "node:fs";
|
|
2
|
-
import { join } from "node:path";
|
|
3
|
-
import * as log from "../lib/log.js";
|
|
4
|
-
|
|
5
|
-
const BASE_PERMISSIONS = [
|
|
6
|
-
"Bash(curl:*)",
|
|
7
|
-
"Bash(python3:*)",
|
|
8
|
-
"WebFetch(domain:registry.mcpay.tech)",
|
|
9
|
-
"WebFetch(domain:frames.ag)",
|
|
10
|
-
"WebSearch",
|
|
11
|
-
];
|
|
12
|
-
|
|
13
|
-
interface ClaudeSettings {
|
|
14
|
-
permissions?: {
|
|
15
|
-
allow?: string[];
|
|
16
|
-
[key: string]: unknown;
|
|
17
|
-
};
|
|
18
|
-
[key: string]: unknown;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export function setupClaude(cwd: string) {
|
|
22
|
-
const claudeDir = join(cwd, ".claude");
|
|
23
|
-
mkdirSync(claudeDir, { recursive: true });
|
|
24
|
-
|
|
25
|
-
const settingsPath = join(claudeDir, "settings.local.json");
|
|
26
|
-
|
|
27
|
-
let settings: ClaudeSettings = {};
|
|
28
|
-
if (existsSync(settingsPath)) {
|
|
29
|
-
try {
|
|
30
|
-
settings = JSON.parse(readFileSync(settingsPath, "utf-8"));
|
|
31
|
-
} catch {
|
|
32
|
-
log.warn("Could not parse existing settings.local.json, creating fresh");
|
|
33
|
-
settings = {};
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
if (!settings.permissions) {
|
|
38
|
-
settings.permissions = {};
|
|
39
|
-
}
|
|
40
|
-
if (!Array.isArray(settings.permissions.allow)) {
|
|
41
|
-
settings.permissions.allow = [];
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
// Merge base permissions (deduplicate)
|
|
45
|
-
const existing = new Set(settings.permissions.allow);
|
|
46
|
-
let added = 0;
|
|
47
|
-
for (const perm of BASE_PERMISSIONS) {
|
|
48
|
-
if (!existing.has(perm)) {
|
|
49
|
-
settings.permissions.allow.push(perm);
|
|
50
|
-
added++;
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n", "utf-8");
|
|
55
|
-
|
|
56
|
-
if (added > 0) {
|
|
57
|
-
log.success(`Added ${added} permission(s) to .claude/settings.local.json`);
|
|
58
|
-
} else {
|
|
59
|
-
log.skip("All base permissions already present");
|
|
60
|
-
}
|
|
61
|
-
}
|
package/tsconfig.json
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"target": "ES2022",
|
|
4
|
-
"module": "ESNext",
|
|
5
|
-
"moduleResolution": "bundler",
|
|
6
|
-
"declaration": true,
|
|
7
|
-
"outDir": "dist",
|
|
8
|
-
"rootDir": "src",
|
|
9
|
-
"strict": true,
|
|
10
|
-
"esModuleInterop": true,
|
|
11
|
-
"skipLibCheck": true,
|
|
12
|
-
"forceConsistentCasingInFileNames": true,
|
|
13
|
-
"resolveJsonModule": true,
|
|
14
|
-
"isolatedModules": true
|
|
15
|
-
},
|
|
16
|
-
"include": ["src"],
|
|
17
|
-
"exclude": ["dist", "node_modules"]
|
|
18
|
-
}
|