@primitive.ai/prim 0.1.0-alpha.7 → 0.1.0-alpha.8
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 +18 -18
- package/dist/index.js +41 -41
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @primitive.ai/prim
|
|
2
2
|
|
|
3
|
-
The official CLI for [Primitive](https://getprimitive.ai). Manage specs, contexts,
|
|
3
|
+
The official CLI for [Primitive](https://getprimitive.ai). Manage specs, contexts, projects, and git hooks from the command line.
|
|
4
4
|
|
|
5
5
|
> [!WARNING]
|
|
6
6
|
> This project is in **alpha**. Commands and APIs may change between releases.
|
|
@@ -45,16 +45,16 @@ prim auth status # Check authentication status
|
|
|
45
45
|
|
|
46
46
|
### Specs
|
|
47
47
|
|
|
48
|
-
Specs are documents that drive implementation. They can be synced to a
|
|
48
|
+
Specs are documents that drive implementation. They can be synced to a project DAG and mapped to file patterns for automatic pre-commit hook integration.
|
|
49
49
|
|
|
50
50
|
```bash
|
|
51
51
|
prim spec list # List all specs
|
|
52
|
-
prim spec list --
|
|
52
|
+
prim spec list --project-id <id> # Find spec for a root project
|
|
53
53
|
prim spec get <id> # Show spec details
|
|
54
54
|
prim spec get <id> --text-only # Print raw spec text
|
|
55
55
|
prim spec update <id> --file spec.md # Update spec from file
|
|
56
56
|
prim spec update <id> --name "New" # Rename a spec
|
|
57
|
-
prim spec sync <id> # Trigger spec-to-
|
|
57
|
+
prim spec sync <id> # Trigger spec-to-project sync
|
|
58
58
|
prim spec map <id> -p "src/auth/**" # Map file patterns to a spec
|
|
59
59
|
prim spec unmap <id> # Clear all file patterns
|
|
60
60
|
prim spec unmap <id> -p "src/auth/**" # Remove specific pattern
|
|
@@ -64,24 +64,24 @@ prim spec auto-map <id> # Auto-detect file patterns
|
|
|
64
64
|
### Contexts
|
|
65
65
|
|
|
66
66
|
```bash
|
|
67
|
-
prim context list
|
|
68
|
-
prim context list --scope
|
|
69
|
-
prim context list --
|
|
70
|
-
prim context get <id>
|
|
71
|
-
prim context create -s
|
|
72
|
-
prim context create -s
|
|
73
|
-
prim context update <id> --name "New"
|
|
74
|
-
prim context delete <id>
|
|
75
|
-
prim context link <id> --
|
|
76
|
-
prim context unlink <id> --
|
|
67
|
+
prim context list # List all contexts
|
|
68
|
+
prim context list --scope project # Filter by scope
|
|
69
|
+
prim context list --project-id <id> # List contexts for a project
|
|
70
|
+
prim context get <id> # Get context details
|
|
71
|
+
prim context create -s project -n "Name" # Create a context
|
|
72
|
+
prim context create -s project -n "Name" --file path/to/file
|
|
73
|
+
prim context update <id> --name "New" # Update a context
|
|
74
|
+
prim context delete <id> # Delete a context
|
|
75
|
+
prim context link <id> --project <pid> # Link context to project
|
|
76
|
+
prim context unlink <id> --project <pid> # Unlink context from project
|
|
77
77
|
```
|
|
78
78
|
|
|
79
|
-
###
|
|
79
|
+
### Projects
|
|
80
80
|
|
|
81
81
|
```bash
|
|
82
|
-
prim
|
|
83
|
-
prim
|
|
84
|
-
prim
|
|
82
|
+
prim project create -n "Project name" # Create a project
|
|
83
|
+
prim project create -n "Project name" -d "Description" # Create with description
|
|
84
|
+
prim project create -n "Project name" --spec <contextId> # Create and link a spec
|
|
85
85
|
```
|
|
86
86
|
|
|
87
87
|
### Hooks
|
package/dist/index.js
CHANGED
|
@@ -237,14 +237,14 @@ async function exchangeCode(siteUrl, code, codeVerifier, redirectUri) {
|
|
|
237
237
|
import { readFileSync as readFileSync2 } from "fs";
|
|
238
238
|
function registerContextCommands(program2) {
|
|
239
239
|
const context = program2.command("context").description("Manage contexts");
|
|
240
|
-
context.command("list").description("List contexts").option("-s, --scope <scope>", "Filter by scope:
|
|
240
|
+
context.command("list").description("List contexts").option("-s, --scope <scope>", "Filter by scope: project, global, external").option("-t, --project-id <projectId>", "List contexts linked to a specific project").action(async (opts) => {
|
|
241
241
|
const client = getClient();
|
|
242
242
|
const params = new URLSearchParams();
|
|
243
|
-
if (opts.
|
|
244
|
-
params.set("taskId", opts.
|
|
243
|
+
if (opts.projectId) {
|
|
244
|
+
params.set("taskId", opts.projectId);
|
|
245
245
|
}
|
|
246
246
|
if (opts.scope) {
|
|
247
|
-
params.set("scope", opts.scope);
|
|
247
|
+
params.set("scope", opts.scope === "project" ? "task" : opts.scope);
|
|
248
248
|
}
|
|
249
249
|
const contexts = await client.get(`/api/cli/contexts?${params.toString()}`);
|
|
250
250
|
printContextList(contexts);
|
|
@@ -254,16 +254,16 @@ function registerContextCommands(program2) {
|
|
|
254
254
|
const ctx = await client.get(`/api/cli/contexts/${contextId}`);
|
|
255
255
|
console.log(JSON.stringify(ctx, null, 2));
|
|
256
256
|
});
|
|
257
|
-
context.command("create").description("Create a new context").requiredOption("-s, --scope <scope>", "Scope:
|
|
257
|
+
context.command("create").description("Create a new context").requiredOption("-s, --scope <scope>", "Scope: project, global, external").requiredOption("-n, --name <name>", "Context name").option("-t, --text <text>", "Context text content").option("-f, --file <path>", "Read text content from file").option("--project-id <projectId>", "Link to project(s), comma-separated").option("--spec", "Mark as a spec document").action(
|
|
258
258
|
async (opts) => {
|
|
259
259
|
const client = getClient();
|
|
260
260
|
let text = opts.text;
|
|
261
261
|
if (opts.file) {
|
|
262
262
|
text = readFileSync2(opts.file, "utf-8");
|
|
263
263
|
}
|
|
264
|
-
const taskIds = opts.
|
|
264
|
+
const taskIds = opts.projectId ? opts.projectId.split(",").map((id) => id.trim()) : void 0;
|
|
265
265
|
const result = await client.post("/api/cli/contexts", {
|
|
266
|
-
scope: opts.scope,
|
|
266
|
+
scope: opts.scope === "project" ? "task" : opts.scope,
|
|
267
267
|
name: opts.name,
|
|
268
268
|
text,
|
|
269
269
|
taskIds,
|
|
@@ -289,19 +289,19 @@ function registerContextCommands(program2) {
|
|
|
289
289
|
await client.delete(`/api/cli/contexts/${contextId}`);
|
|
290
290
|
console.log(`Deleted context: ${contextId}`);
|
|
291
291
|
});
|
|
292
|
-
context.command("link <contextId>").description("Link a context to a
|
|
292
|
+
context.command("link <contextId>").description("Link a context to a project").requiredOption("--project <projectId>", "Project ID to link to").action(async (contextId, opts) => {
|
|
293
293
|
const client = getClient();
|
|
294
294
|
await client.post(`/api/cli/contexts/${contextId}/link`, {
|
|
295
|
-
taskId: opts.
|
|
295
|
+
taskId: opts.project
|
|
296
296
|
});
|
|
297
|
-
console.log(`Linked context ${contextId} to
|
|
297
|
+
console.log(`Linked context ${contextId} to project ${opts.project}`);
|
|
298
298
|
});
|
|
299
|
-
context.command("unlink <contextId>").description("Unlink a context from a
|
|
299
|
+
context.command("unlink <contextId>").description("Unlink a context from a project").requiredOption("--project <projectId>", "Project ID to unlink from").action(async (contextId, opts) => {
|
|
300
300
|
const client = getClient();
|
|
301
301
|
await client.post(`/api/cli/contexts/${contextId}/unlink`, {
|
|
302
|
-
taskId: opts.
|
|
302
|
+
taskId: opts.project
|
|
303
303
|
});
|
|
304
|
-
console.log(`Unlinked context ${contextId} from
|
|
304
|
+
console.log(`Unlinked context ${contextId} from project ${opts.project}`);
|
|
305
305
|
});
|
|
306
306
|
}
|
|
307
307
|
function printContextList(contexts) {
|
|
@@ -310,7 +310,7 @@ function printContextList(contexts) {
|
|
|
310
310
|
return;
|
|
311
311
|
}
|
|
312
312
|
for (const ctx of contexts) {
|
|
313
|
-
const scope = ctx.scope
|
|
313
|
+
const scope = ctx.scope === "task" ? "project" : ctx.scope ?? "project";
|
|
314
314
|
const spec = ctx.isSpecDocument ? " [SPEC]" : "";
|
|
315
315
|
const name = ctx.name ?? ctx.title ?? "(unnamed)";
|
|
316
316
|
console.log(`${ctx._id} ${scope.padEnd(8)} ${name}${spec}`);
|
|
@@ -456,16 +456,33 @@ function registerHooksCommands(program2) {
|
|
|
456
456
|
});
|
|
457
457
|
}
|
|
458
458
|
|
|
459
|
+
// src/commands/project.ts
|
|
460
|
+
function registerProjectCommands(program2) {
|
|
461
|
+
const project = program2.command("project").description("Manage projects");
|
|
462
|
+
project.command("create").description("Create a new project").requiredOption("-n, --name <name>", "Project name").option("-d, --description <description>", "Project description").option("--spec <contextId>", "Link an existing spec as this project's spec").action(async (opts) => {
|
|
463
|
+
const client = getClient();
|
|
464
|
+
const result = await client.post("/api/cli/tasks", {
|
|
465
|
+
name: opts.name,
|
|
466
|
+
description: opts.description,
|
|
467
|
+
specContextId: opts.spec
|
|
468
|
+
});
|
|
469
|
+
console.log(`Created project: ${result._id}`);
|
|
470
|
+
if (opts.spec) {
|
|
471
|
+
console.log(`Linked spec: ${opts.spec}`);
|
|
472
|
+
}
|
|
473
|
+
});
|
|
474
|
+
}
|
|
475
|
+
|
|
459
476
|
// src/commands/spec.ts
|
|
460
477
|
import { readFileSync as readFileSync4 } from "fs";
|
|
461
478
|
function registerSpecCommands(program2) {
|
|
462
479
|
const spec = program2.command("spec").description("Manage spec documents");
|
|
463
|
-
spec.command("list").description("List spec documents").option("-t, --
|
|
480
|
+
spec.command("list").description("List spec documents").option("-t, --project-id <projectId>", "List spec for a specific root project").action(async (opts) => {
|
|
464
481
|
const client = getClient();
|
|
465
|
-
if (opts.
|
|
466
|
-
const specs = await client.get(`/api/cli/specs?rootTaskId=${opts.
|
|
482
|
+
if (opts.projectId) {
|
|
483
|
+
const specs = await client.get(`/api/cli/specs?rootTaskId=${opts.projectId}`);
|
|
467
484
|
if (specs.length === 0) {
|
|
468
|
-
console.log("No spec document found for this
|
|
485
|
+
console.log("No spec document found for this project.");
|
|
469
486
|
return;
|
|
470
487
|
}
|
|
471
488
|
printSpec(specs[0]);
|
|
@@ -477,7 +494,7 @@ function registerSpecCommands(program2) {
|
|
|
477
494
|
return;
|
|
478
495
|
}
|
|
479
496
|
for (const ctx of contexts) {
|
|
480
|
-
const scope = ctx.scope
|
|
497
|
+
const scope = ctx.scope === "task" ? "project" : ctx.scope ?? "project";
|
|
481
498
|
const review = ctx.specReviewStatus ?? "\u2014";
|
|
482
499
|
const name = ctx.name ?? "(unnamed)";
|
|
483
500
|
console.log(`${ctx._id} ${scope.padEnd(8)} ${String(review).padEnd(10)} ${name}`);
|
|
@@ -514,7 +531,7 @@ ${contexts.length} spec(s)`);
|
|
|
514
531
|
}
|
|
515
532
|
console.log(`Updated spec: ${contextId}`);
|
|
516
533
|
});
|
|
517
|
-
spec.command("sync <contextId>").description("Trigger spec \u2194
|
|
534
|
+
spec.command("sync <contextId>").description("Trigger spec \u2194 project DAG synchronization").action(async (contextId) => {
|
|
518
535
|
const client = getClient();
|
|
519
536
|
const ctx = await client.get(`/api/cli/contexts/${contextId}`);
|
|
520
537
|
if (!ctx.isSpecDocument) {
|
|
@@ -524,7 +541,7 @@ ${contexts.length} spec(s)`);
|
|
|
524
541
|
await client.post(`/api/cli/contexts/${contextId}/sync`);
|
|
525
542
|
console.log(`Triggered sync for spec: ${contextId}`);
|
|
526
543
|
if (ctx.specRootTaskId) {
|
|
527
|
-
console.log(`Root
|
|
544
|
+
console.log(`Root project: ${ctx.specRootTaskId}`);
|
|
528
545
|
}
|
|
529
546
|
});
|
|
530
547
|
spec.command("map <contextId>").description("Map file patterns to a spec (used by pre-commit hook to detect affected specs)").requiredOption(
|
|
@@ -566,9 +583,9 @@ function printSpec(ctx) {
|
|
|
566
583
|
const patterns = ctx.filePatterns;
|
|
567
584
|
console.log(`ID: ${ctx._id}`);
|
|
568
585
|
console.log(`Name: ${name}`);
|
|
569
|
-
console.log(`Scope: ${ctx.scope
|
|
586
|
+
console.log(`Scope: ${ctx.scope === "task" ? "project" : ctx.scope ?? "project"}`);
|
|
570
587
|
console.log(`Review Status: ${review}`);
|
|
571
|
-
console.log(`Root
|
|
588
|
+
console.log(`Root Project: ${ctx.specRootTaskId ?? "\u2014"}`);
|
|
572
589
|
console.log(`Sync Version: ${ctx.syncVersion ?? 0}`);
|
|
573
590
|
console.log(`Index Status: ${ctx.indexStatus ?? "\u2014"}`);
|
|
574
591
|
console.log(`File Patterns: ${patterns?.length ? patterns.join(", ") : "\u2014"}`);
|
|
@@ -581,23 +598,6 @@ ${preview}`);
|
|
|
581
598
|
}
|
|
582
599
|
}
|
|
583
600
|
|
|
584
|
-
// src/commands/task.ts
|
|
585
|
-
function registerTaskCommands(program2) {
|
|
586
|
-
const task = program2.command("task").description("Manage tasks");
|
|
587
|
-
task.command("create").description("Create a new task").requiredOption("-n, --name <name>", "Task name").option("-d, --description <description>", "Task description").option("--spec <contextId>", "Link an existing spec as this task's spec").action(async (opts) => {
|
|
588
|
-
const client = getClient();
|
|
589
|
-
const result = await client.post("/api/cli/tasks", {
|
|
590
|
-
name: opts.name,
|
|
591
|
-
description: opts.description,
|
|
592
|
-
specContextId: opts.spec
|
|
593
|
-
});
|
|
594
|
-
console.log(`Created task: ${result._id}`);
|
|
595
|
-
if (opts.spec) {
|
|
596
|
-
console.log(`Linked spec: ${opts.spec}`);
|
|
597
|
-
}
|
|
598
|
-
});
|
|
599
|
-
}
|
|
600
|
-
|
|
601
601
|
// src/index.ts
|
|
602
602
|
var __dirname = dirname2(fileURLToPath(import.meta.url));
|
|
603
603
|
var pkg = JSON.parse(readFileSync5(resolve2(__dirname, "../package.json"), "utf-8"));
|
|
@@ -606,7 +606,7 @@ program.name("prim").description("CLI for managing Primitive specs and contexts"
|
|
|
606
606
|
registerAuthCommands(program);
|
|
607
607
|
registerContextCommands(program);
|
|
608
608
|
registerSpecCommands(program);
|
|
609
|
-
|
|
609
|
+
registerProjectCommands(program);
|
|
610
610
|
registerHooksCommands(program);
|
|
611
611
|
process.on("unhandledRejection", (err) => {
|
|
612
612
|
const msg = err instanceof Error ? err.message : String(err);
|