sfora-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/README.md CHANGED
@@ -7,6 +7,7 @@ or `echo >` straight into it. Ships first-class verbs, an interactive shell, and
7
7
  an **MCP server** (`--mcp`) so agents in Claude / Cursor operate sfora natively.
8
8
 
9
9
  ```bash
10
+ sfora new "Acme" # create a project
10
11
  sfora post plan.md --project acme # push a markdown plan as a post
11
12
  sfora task spec.md --project acme # …or a task on the board
12
13
  sfora cat /projects/acme/board/01-todo/0042-fix-login.md
@@ -70,6 +70,11 @@ export declare class SforaApiClient {
70
70
  constructor(config: SforaApiConfig);
71
71
  /** `GET /v1/fs/projects` */
72
72
  listProjects(): Promise<Project[]>;
73
+ /** `POST /v1/fs/projects` — create a project; the caller becomes its lead. */
74
+ createProject(name: string): Promise<{
75
+ slug: string;
76
+ name: string;
77
+ }>;
73
78
  /**
74
79
  * `GET …/posts` or `…/drafts`. `scheduled` lists drafts that have a
75
80
  * (future) `scheduledFor` set.
@@ -85,6 +85,11 @@ export class SforaApiClient {
85
85
  const data = await this.#json("/v1/fs/projects");
86
86
  return data.projects;
87
87
  }
88
+ /** `POST /v1/fs/projects` — create a project; the caller becomes its lead. */
89
+ async createProject(name) {
90
+ const res = await this.#request("POST", "/v1/fs/projects", JSON.stringify({ name }));
91
+ return (await res.json());
92
+ }
88
93
  /**
89
94
  * `GET …/posts` or `…/drafts`. `scheduled` lists drafts that have a
90
95
  * (future) `scheduledFor` set.
package/dist/cli.js CHANGED
@@ -10,7 +10,7 @@ import * as readline from "node:readline";
10
10
  import { spawn } from "node:child_process";
11
11
  import { readFile as readLocalFile } from "node:fs/promises";
12
12
  import { basename } from "node:path";
13
- import { createSforaShell } from "./index.js";
13
+ import { createSforaShell, SforaApiClient } from "./index.js";
14
14
  import { runMcpServer } from "./mcp-server.js";
15
15
  import { readConfig, writeConfig, resolveSettings, DEFAULT_URL, } from "./config.js";
16
16
  const colors = {
@@ -89,8 +89,11 @@ Push markdown straight to sfora:
89
89
  sfora task <file.md> [--project <slug>] [--column <name>] Create a task
90
90
  sfora doc <file.md> [--project <slug>] Create a doc
91
91
 
92
- Browse:
92
+ Projects:
93
+ sfora new "<name>" Create a project
93
94
  sfora projects List your projects
95
+
96
+ Browse:
94
97
  sfora ls [path] List a path (default /projects)
95
98
  sfora cat <path> Print a file's markdown
96
99
  sfora Open the interactive shell
@@ -260,14 +263,30 @@ async function resolveProject(fs, flag, md) {
260
263
  return slugs[0];
261
264
  throw new Error(`specify --project <slug>${slugs.length ? ` (yours: ${slugs.join(", ")})` : ""}`);
262
265
  }
263
- const VERBS = new Set(["projects", "ls", "cat", "post", "task", "doc"]);
264
- async function runVerb(args, fs) {
266
+ const VERBS = new Set([
267
+ "projects",
268
+ "new",
269
+ "ls",
270
+ "cat",
271
+ "post",
272
+ "task",
273
+ "doc",
274
+ ]);
275
+ async function runVerb(args, fs, client) {
265
276
  const ok = (msg) => console.log(`${colors.green}✓${colors.reset} ${msg}`);
266
277
  if (args.command === "projects") {
267
278
  const slugs = await fs.readdir("/projects");
268
279
  console.log(slugs.length ? slugs.join("\n") : "(no projects)");
269
280
  return;
270
281
  }
282
+ if (args.command === "new") {
283
+ const name = args.rest.join(" ").trim();
284
+ if (!name)
285
+ throw new Error('usage: sfora new "<project name>"');
286
+ const res = await client.createProject(name);
287
+ ok(`Created project ${res.name} · /projects/${res.slug}`);
288
+ return;
289
+ }
271
290
  if (args.command === "ls") {
272
291
  const entries = await fs.readdir(args.rest[0] ?? "/projects");
273
292
  console.log(entries.join("\n"));
@@ -354,8 +373,9 @@ async function main() {
354
373
  apiKey,
355
374
  org: settings.org ?? "",
356
375
  });
376
+ const client = new SforaApiClient({ baseUrl, apiKey });
357
377
  try {
358
- await runVerb(args, fs);
378
+ await runVerb(args, fs, client);
359
379
  }
360
380
  catch (e) {
361
381
  process.stderr.write(`${colors.red}error:${colors.reset} ${e instanceof Error ? e.message : String(e)}\n`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sfora-cli",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "type": "module",
5
5
  "description": "Your sfora workspace as a markdown filesystem — a CLI + MCP server. Post/task/doc, ls/cat/grep, and a shell so agents operate sfora natively.",
6
6
  "keywords": [