@primitive.ai/prim 0.1.0-alpha.11 → 0.1.0-alpha.13
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/SKILL.md +132 -0
- package/dist/index.js +160 -9
- package/package.json +8 -3
package/SKILL.md
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: prim
|
|
3
|
+
description: Use the prim CLI for managing Primitive specs, contexts, projects, and pre-commit hooks. TRIGGER when the user mentions Primitive, prim, "specs" (in the Primitive sense), or "contexts" (in the Primitive sense); when the repo's package.json depends on @primitive.ai/prim; when the user asks to sync, map, update, or auto-map a spec; when configuring Primitive pre-commit hooks. SKIP when "spec" means test specs (vitest, jest, rspec), when "context" means React context or LLM context window, or for unrelated CLIs.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Working with the prim CLI
|
|
7
|
+
|
|
8
|
+
`prim` is the official CLI for [Primitive](https://app.getprimitive.ai). Use it -- don't reach for shell or curl.
|
|
9
|
+
|
|
10
|
+
## Mental model
|
|
11
|
+
|
|
12
|
+
A **spec** captures intent for execution -- it defines what should be done, usually so other agents (or humans) can act on it. A **context** is everything else: supporting material that informs but doesn't define the work -- design docs, references, prior art, shared documentation, examples. When deciding which to create, ask: does this say *what to do*, or does it *inform* whoever's doing it? A project has at most one spec but can link many contexts.
|
|
13
|
+
|
|
14
|
+
In Primitive, a markdown spec is associated with a **project**. The spec is the source of truth: `npx --yes @primitive.ai/prim spec sync` parses the spec, diffs it against the project, and **applies the diff** -- adding, updating, or **archiving** items in the project to match. Items removed from a spec are soft-archived (recoverable via the dashboard), not deleted -- but they leave the active view, so flag the user before large spec rewrites on projects with work in flight.
|
|
15
|
+
|
|
16
|
+
A **spec is a kind of context** -- same IDs, same storage. The `npx --yes @primitive.ai/prim spec ...` commands are a focused view onto specs; `npx --yes @primitive.ai/prim context get <id>` works on a spec ID and vice versa. For structured metadata on a spec (review status, root project, sync version, scope, file patterns), use `npx --yes @primitive.ai/prim context get <specId>` -- it returns JSON.
|
|
17
|
+
|
|
18
|
+
`npx --yes @primitive.ai/prim spec list` returns only spec-type contexts. `npx --yes @primitive.ai/prim context list` returns all contexts regardless of type.
|
|
19
|
+
|
|
20
|
+
## Auth
|
|
21
|
+
|
|
22
|
+
Run `npx --yes @primitive.ai/prim auth status` first. It exits **0 if authenticated, 1 if not** -- branch on the exit code, don't parse the message.
|
|
23
|
+
|
|
24
|
+
Three ways to authenticate, in priority order:
|
|
25
|
+
|
|
26
|
+
1. **`PRIM_TOKEN` environment variable** -- preferred for agents and CI. Set it before invoking prim and you're done; no interactive flow, no token files.
|
|
27
|
+
2. **`npx --yes @primitive.ai/prim auth set-token <token>`** -- saves a bearer token to `~/.config/prim/token`. Use when the user has a long-lived token in hand.
|
|
28
|
+
3. **`npx --yes @primitive.ai/prim auth login`** -- opens a browser via WorkOS OAuth. **An agent cannot complete this.** If `auth status` exits non-zero and `PRIM_TOKEN` is unset, **stop and ask the user** to run `npx --yes @primitive.ai/prim auth login` themselves.
|
|
29
|
+
|
|
30
|
+
The CLI auto-refreshes expired tokens. On unrecoverable expiry it throws `Authentication expired. Run prim auth login to re-authenticate.` -- relay it.
|
|
31
|
+
|
|
32
|
+
## Ground rules
|
|
33
|
+
|
|
34
|
+
1. Don't guess IDs. Discover them with `npx --yes @primitive.ai/prim spec list`, `npx --yes @primitive.ai/prim spec list --project-id <pid>`, or `npx --yes @primitive.ai/prim context list`.
|
|
35
|
+
2. Every command accepts `--help`. When unsure of flags, run `npx --yes @primitive.ai/prim <cmd> --help` rather than guessing.
|
|
36
|
+
3. The CLI prints API errors as one-liners to stderr and exits non-zero. Treat any non-zero exit as actionable. If a command fails with an unrecognized error, re-run with `--help` to check your flags. If auth-related, re-check `auth status`.
|
|
37
|
+
|
|
38
|
+
## Common workflows
|
|
39
|
+
|
|
40
|
+
### Read a spec's current text (do this before any partial edit)
|
|
41
|
+
```
|
|
42
|
+
npx --yes @primitive.ai/prim spec get <id> --text-only > spec.md
|
|
43
|
+
```
|
|
44
|
+
`npx --yes @primitive.ai/prim spec update <id> --file <path>` replaces the entire body. Fetch first if you're only changing part of it.
|
|
45
|
+
|
|
46
|
+
### Update a spec from a local file and apply to the project
|
|
47
|
+
```
|
|
48
|
+
npx --yes @primitive.ai/prim spec list --project-id <pid> # find the spec for a project
|
|
49
|
+
npx --yes @primitive.ai/prim spec update <id> --file spec.md # replaces spec body
|
|
50
|
+
npx --yes @primitive.ai/prim spec sync <id> # required -- update doesn't apply changes to the project
|
|
51
|
+
```
|
|
52
|
+
`npx --yes @primitive.ai/prim spec sync` is **async**: it returns immediately with `Triggered sync for spec`, then applies in the background. The project isn't updated when the command returns -- surface that to the user.
|
|
53
|
+
|
|
54
|
+
Auto-map runs automatically on the server after every `spec update`. Call `npx --yes @primitive.ai/prim spec auto-map <id>` explicitly only to re-run mapping without changing the spec text.
|
|
55
|
+
|
|
56
|
+
### Map files to a spec (so pre-commit auto-syncs all affected specs)
|
|
57
|
+
```
|
|
58
|
+
npx --yes @primitive.ai/prim spec map <id> -p "src/auth/**" "src/foo/**" # multiple patterns at once
|
|
59
|
+
npx --yes @primitive.ai/prim spec unmap <id> -p "src/auth/**" # remove one
|
|
60
|
+
npx --yes @primitive.ai/prim spec unmap <id> # clear all manual patterns
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Create or link a context
|
|
64
|
+
```
|
|
65
|
+
npx --yes @primitive.ai/prim context create -s project -n "<name>" --file <path> --project-id <pid> # add --spec to make it a spec
|
|
66
|
+
npx --yes @primitive.ai/prim context create -s global -n "<name>" --text "..." # filed in the global context pane, not linked to a specific project
|
|
67
|
+
npx --yes @primitive.ai/prim context link <ctxId> --project <projectId> # works on any scope
|
|
68
|
+
npx --yes @primitive.ai/prim context unlink <ctxId> --project <projectId> # remove a link
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Update or delete a context
|
|
72
|
+
```
|
|
73
|
+
npx --yes @primitive.ai/prim context update <id> -n "<new name>" # rename
|
|
74
|
+
npx --yes @primitive.ai/prim context update <id> --file <path> # replace body
|
|
75
|
+
npx --yes @primitive.ai/prim context delete <id> # permanent -- confirm with the user first
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Create a project (optionally with a linked spec)
|
|
79
|
+
```
|
|
80
|
+
npx --yes @primitive.ai/prim project create -n "<name>" -d "<desc>"
|
|
81
|
+
npx --yes @primitive.ai/prim project create -n "<name>" --spec <contextId> # value is a context ID
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Install the pre-commit hook
|
|
85
|
+
```
|
|
86
|
+
npx --yes @primitive.ai/prim hooks install # auto-detects Husky and prompts
|
|
87
|
+
npx --yes @primitive.ai/prim hooks uninstall
|
|
88
|
+
```
|
|
89
|
+
**Note:** `hooks uninstall` only removes `.git/hooks/pre-commit`. If the hook was installed into `.husky/pre-commit`, you must remove the prim block from that file manually.
|
|
90
|
+
|
|
91
|
+
## How the pre-commit hook behaves
|
|
92
|
+
|
|
93
|
+
`npx --yes @primitive.ai/prim hooks install` adds a hook that, on every commit:
|
|
94
|
+
|
|
95
|
+
1. Fetches the org's spec-to-file-pattern mappings.
|
|
96
|
+
2. Glob-matches staged files against each spec's patterns (`*` and `**` supported).
|
|
97
|
+
3. For each affected spec, sends `git diff --cached` to `/api/cli/contexts/:id/sync-diff`. The backend runs an **LLM over (current spec + diff)** to produce edits, updates the spec text, then applies the new spec to the project.
|
|
98
|
+
4. Prints `[synced] <id> -- <name>` or `[skip] <id> -- <reason>` per affected spec to stdout, and `[error]` lines to stderr.
|
|
99
|
+
|
|
100
|
+
What that means:
|
|
101
|
+
|
|
102
|
+
- **The hook is not `npx --yes @primitive.ai/prim spec sync`.** `npx --yes @primitive.ai/prim spec sync` re-applies the *existing* spec to the project. The hook calls `sync-diff` -- an LLM updates the spec from the code change, then applies the new spec to the project. The casual "just commit and the hook will sync" is ambiguous; when explaining to the user, specify which operation you mean.
|
|
103
|
+
- **The hook never blocks the commit.** Failures (auth, network, backend) print `[error]` to stderr but exit 0, so a successful `git commit` doesn't prove the spec changed. Check the hook's `[synced]` / `[error]` / `[skip]` output, or verify with `npx --yes @primitive.ai/prim spec get <id>`.
|
|
104
|
+
- **Diffs over 256 KiB are truncated.** The hook logs `(truncated: X KiB -> Y KiB analyzed)`. The LLM only sees the first 256 KiB of the diff.
|
|
105
|
+
- **To suppress the hook for one commit** (e.g., when intentionally desyncing code from spec, or when committing unrelated changes), use `git commit --no-verify`.
|
|
106
|
+
|
|
107
|
+
## Output formats
|
|
108
|
+
|
|
109
|
+
| Command | Output | Where the ID is |
|
|
110
|
+
|---|---|---|
|
|
111
|
+
| `npx --yes @primitive.ai/prim context create` | `Created context: <id>` | Match `^Created context: (\S+)` |
|
|
112
|
+
| `npx --yes @primitive.ai/prim project create` | `Created project: <id>` | Match `^Created project: (\S+)` |
|
|
113
|
+
| `npx --yes @primitive.ai/prim spec update` | `Updated spec: <id>` | Match `^Updated spec: (\S+)` |
|
|
114
|
+
| `npx --yes @primitive.ai/prim spec sync` | `Triggered sync for spec: <id>` | Match `^Triggered sync for spec: (\S+)` |
|
|
115
|
+
| `npx --yes @primitive.ai/prim context list`, `npx --yes @primitive.ai/prim spec list` | Table with trailing count line | First token of each row (skip the final `N spec(s)` / `N context(s)` summary line) |
|
|
116
|
+
| `npx --yes @primitive.ai/prim spec list --project-id <pid>` | Single-spec block (key:value) | `ID:` line |
|
|
117
|
+
| `npx --yes @primitive.ai/prim context get <id>` | Pretty-printed JSON | `._id` field |
|
|
118
|
+
| `npx --yes @primitive.ai/prim spec get <id>` | Human-readable key:value block | `ID:` line |
|
|
119
|
+
| `npx --yes @primitive.ai/prim spec get <id> --text-only` | Raw spec markdown, nothing else | n/a |
|
|
120
|
+
|
|
121
|
+
## Pitfalls
|
|
122
|
+
|
|
123
|
+
- **`npx --yes @primitive.ai/prim spec sync` archives anything dropped from the spec.** Removed content is archived (recoverable), not deleted.
|
|
124
|
+
- **`npx --yes @primitive.ai/prim spec update` doesn't apply changes to the project.** Always follow with `npx --yes @primitive.ai/prim spec sync <id>`.
|
|
125
|
+
- **`npx --yes @primitive.ai/prim spec update --file` replaces the whole body.** Fetch with `npx --yes @primitive.ai/prim spec get <id> --text-only` before any partial edit.
|
|
126
|
+
- **`npx --yes @primitive.ai/prim spec sync` rejects non-spec contexts** with "Context is not a spec document. Use `prim context` instead." Use `npx --yes @primitive.ai/prim spec list` to find spec IDs.
|
|
127
|
+
- **`npx --yes @primitive.ai/prim context delete` is permanent.** Confirm with the user before deleting.
|
|
128
|
+
- **Scope is set at creation.** To change it, delete and recreate the context.
|
|
129
|
+
|
|
130
|
+
## After each task
|
|
131
|
+
|
|
132
|
+
Report the names and IDs you touched (spec, context, project) so the user can verify in the dashboard. If you ran `npx --yes @primitive.ai/prim spec sync`, remind the user it's async -- the project settles in the background.
|
package/dist/index.js
CHANGED
|
@@ -11,10 +11,11 @@ import {
|
|
|
11
11
|
} from "./chunk-3APLWTLB.js";
|
|
12
12
|
|
|
13
13
|
// src/index.ts
|
|
14
|
-
import { readFileSync as
|
|
15
|
-
import { dirname as
|
|
16
|
-
import { fileURLToPath } from "url";
|
|
14
|
+
import { readFileSync as readFileSync6 } from "fs";
|
|
15
|
+
import { dirname as dirname3, resolve as resolve3 } from "path";
|
|
16
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
17
17
|
import { Command } from "commander";
|
|
18
|
+
import updateNotifier from "update-notifier";
|
|
18
19
|
|
|
19
20
|
// src/commands/auth.ts
|
|
20
21
|
import { exec } from "child_process";
|
|
@@ -103,10 +104,10 @@ function registerAuthCommands(program2) {
|
|
|
103
104
|
process.exit(1);
|
|
104
105
|
});
|
|
105
106
|
});
|
|
106
|
-
const port = await new Promise((
|
|
107
|
+
const port = await new Promise((resolve4) => {
|
|
107
108
|
server.listen(CALLBACK_PORT, LOCALHOST, () => {
|
|
108
109
|
const addr = server.address();
|
|
109
|
-
|
|
110
|
+
resolve4(typeof addr === "object" && addr ? addr.port : 0);
|
|
110
111
|
});
|
|
111
112
|
});
|
|
112
113
|
const redirectUri = `http://${LOCALHOST}:${port}/callback`;
|
|
@@ -473,8 +474,156 @@ function registerProjectCommands(program2) {
|
|
|
473
474
|
});
|
|
474
475
|
}
|
|
475
476
|
|
|
477
|
+
// src/commands/skill.ts
|
|
478
|
+
import {
|
|
479
|
+
closeSync,
|
|
480
|
+
existsSync as existsSync3,
|
|
481
|
+
fsyncSync,
|
|
482
|
+
openSync,
|
|
483
|
+
readFileSync as readFileSync4,
|
|
484
|
+
renameSync,
|
|
485
|
+
writeFileSync as writeFileSync3
|
|
486
|
+
} from "fs";
|
|
487
|
+
import { dirname as dirname2, resolve as resolve2 } from "path";
|
|
488
|
+
import { fileURLToPath } from "url";
|
|
489
|
+
import { createPatch } from "diff";
|
|
490
|
+
var __dirname = dirname2(fileURLToPath(import.meta.url));
|
|
491
|
+
var SKILL_BEGIN = "<!-- BEGIN PRIM SKILL v1 -->";
|
|
492
|
+
var SKILL_END = "<!-- END PRIM SKILL v1 -->";
|
|
493
|
+
var TARGET_CANDIDATES = [
|
|
494
|
+
"CLAUDE.md",
|
|
495
|
+
".cursor/rules",
|
|
496
|
+
".windsurfrules",
|
|
497
|
+
".github/instructions/primitive.md"
|
|
498
|
+
];
|
|
499
|
+
var DEFAULT_TARGET = "CLAUDE.md";
|
|
500
|
+
function loadSkill() {
|
|
501
|
+
let dir = __dirname;
|
|
502
|
+
while (dir !== dirname2(dir)) {
|
|
503
|
+
const p = resolve2(dir, "SKILL.md");
|
|
504
|
+
if (existsSync3(p)) return readFileSync4(p, "utf-8");
|
|
505
|
+
dir = dirname2(dir);
|
|
506
|
+
}
|
|
507
|
+
throw new Error("SKILL.md not found in package");
|
|
508
|
+
}
|
|
509
|
+
function detectTargets(cwd) {
|
|
510
|
+
return TARGET_CANDIDATES.filter((p) => existsSync3(resolve2(cwd, p)));
|
|
511
|
+
}
|
|
512
|
+
function detectNewline(content) {
|
|
513
|
+
return content.includes("\r\n") ? "\r\n" : "\n";
|
|
514
|
+
}
|
|
515
|
+
function composeBlock(skill, eol) {
|
|
516
|
+
const body = skill.replace(/\r?\n/g, eol);
|
|
517
|
+
return `${SKILL_BEGIN}${eol}${body}${eol}${SKILL_END}`;
|
|
518
|
+
}
|
|
519
|
+
function applyBlock(existing, block, eol) {
|
|
520
|
+
const b = existing.indexOf(SKILL_BEGIN);
|
|
521
|
+
const e = existing.indexOf(SKILL_END);
|
|
522
|
+
if (b !== -1 && e !== -1) {
|
|
523
|
+
return existing.slice(0, b) + block + existing.slice(e + SKILL_END.length);
|
|
524
|
+
}
|
|
525
|
+
if (existing.length === 0) return `${block}${eol}`;
|
|
526
|
+
const sep = existing.endsWith(eol) ? "" : eol;
|
|
527
|
+
return `${existing}${sep}${block}${eol}`;
|
|
528
|
+
}
|
|
529
|
+
function removeBlock(existing) {
|
|
530
|
+
const b = existing.indexOf(SKILL_BEGIN);
|
|
531
|
+
const e = existing.indexOf(SKILL_END);
|
|
532
|
+
if (b === -1 || e === -1) return null;
|
|
533
|
+
const out = existing.slice(0, b) + existing.slice(e + SKILL_END.length);
|
|
534
|
+
return out.replace(/(\r?\n){2,}$/, "$1");
|
|
535
|
+
}
|
|
536
|
+
function atomicWrite(target, content) {
|
|
537
|
+
const tmp = `${target}.tmp`;
|
|
538
|
+
writeFileSync3(tmp, content);
|
|
539
|
+
const fd = openSync(tmp, "r+");
|
|
540
|
+
try {
|
|
541
|
+
fsyncSync(fd);
|
|
542
|
+
} finally {
|
|
543
|
+
closeSync(fd);
|
|
544
|
+
}
|
|
545
|
+
renameSync(tmp, target);
|
|
546
|
+
}
|
|
547
|
+
function resolveTarget(cwd, override) {
|
|
548
|
+
if (override) return resolve2(cwd, override);
|
|
549
|
+
const matches = detectTargets(cwd);
|
|
550
|
+
if (matches.length === 0) return resolve2(cwd, DEFAULT_TARGET);
|
|
551
|
+
if (matches.length === 1) return resolve2(cwd, matches[0]);
|
|
552
|
+
console.error("Multiple rules files detected. Use --target to disambiguate:");
|
|
553
|
+
for (const m of matches) console.error(` ${m}`);
|
|
554
|
+
return null;
|
|
555
|
+
}
|
|
556
|
+
function runInstall(cwd, opts) {
|
|
557
|
+
const target = resolveTarget(cwd, opts.target);
|
|
558
|
+
if (target === null) return 1;
|
|
559
|
+
const existing = existsSync3(target) ? readFileSync4(target, "utf-8") : "";
|
|
560
|
+
const eol = existing ? detectNewline(existing) : "\n";
|
|
561
|
+
const block = composeBlock(loadSkill(), eol);
|
|
562
|
+
const next = applyBlock(existing, block, eol);
|
|
563
|
+
if (next === existing) {
|
|
564
|
+
console.log("No changes \u2014 skill block already up to date.");
|
|
565
|
+
return 0;
|
|
566
|
+
}
|
|
567
|
+
if (opts.dryRun) {
|
|
568
|
+
process.stdout.write(createPatch(target, existing, next, "current", "proposed"));
|
|
569
|
+
return 0;
|
|
570
|
+
}
|
|
571
|
+
atomicWrite(target, next);
|
|
572
|
+
console.log(`Wrote ${Buffer.byteLength(next)} bytes to ${target}`);
|
|
573
|
+
return 0;
|
|
574
|
+
}
|
|
575
|
+
function runUninstall(cwd, opts) {
|
|
576
|
+
const target = resolveTarget(cwd, opts.target);
|
|
577
|
+
if (target === null) return 1;
|
|
578
|
+
if (!existsSync3(target)) {
|
|
579
|
+
console.log(`Skill block not present at ${target}`);
|
|
580
|
+
return 0;
|
|
581
|
+
}
|
|
582
|
+
const existing = readFileSync4(target, "utf-8");
|
|
583
|
+
const next = removeBlock(existing);
|
|
584
|
+
if (next === null) {
|
|
585
|
+
console.log(`Skill block not present at ${target}`);
|
|
586
|
+
return 0;
|
|
587
|
+
}
|
|
588
|
+
atomicWrite(target, next);
|
|
589
|
+
console.log(`Removed skill block from ${target}`);
|
|
590
|
+
return 0;
|
|
591
|
+
}
|
|
592
|
+
function runStatus(cwd, opts) {
|
|
593
|
+
const target = resolveTarget(cwd, opts.target);
|
|
594
|
+
if (target === null) return 1;
|
|
595
|
+
if (!existsSync3(target)) {
|
|
596
|
+
console.log(`No rules file at ${target}`);
|
|
597
|
+
return 1;
|
|
598
|
+
}
|
|
599
|
+
const content = readFileSync4(target, "utf-8");
|
|
600
|
+
if (content.includes(SKILL_BEGIN) && content.includes(SKILL_END)) {
|
|
601
|
+
console.log(`PRIM SKILL v1 installed at ${target}`);
|
|
602
|
+
return 0;
|
|
603
|
+
}
|
|
604
|
+
console.log(`No PRIM SKILL block at ${target}`);
|
|
605
|
+
return 1;
|
|
606
|
+
}
|
|
607
|
+
function registerSkillCommands(program2) {
|
|
608
|
+
const skill = program2.command("skill").description("Manage the prim skill in your project rules file");
|
|
609
|
+
skill.command("install").description("Install the prim skill block into your project rules file").option("--target <path>", "Path to the rules file (overrides auto-detection)").option("--dry-run", "Print a unified diff without writing").action((opts) => {
|
|
610
|
+
try {
|
|
611
|
+
process.exit(runInstall(process.cwd(), opts));
|
|
612
|
+
} catch (err) {
|
|
613
|
+
console.error(err instanceof Error ? err.message : String(err));
|
|
614
|
+
process.exit(2);
|
|
615
|
+
}
|
|
616
|
+
});
|
|
617
|
+
skill.command("uninstall").description("Remove the prim skill block from your project rules file").option("--target <path>", "Path to the rules file (overrides auto-detection)").action((opts) => {
|
|
618
|
+
process.exit(runUninstall(process.cwd(), opts));
|
|
619
|
+
});
|
|
620
|
+
skill.command("status").description("Report whether the prim skill block is installed").option("--target <path>", "Path to the rules file (overrides auto-detection)").action((opts) => {
|
|
621
|
+
process.exit(runStatus(process.cwd(), opts));
|
|
622
|
+
});
|
|
623
|
+
}
|
|
624
|
+
|
|
476
625
|
// src/commands/spec.ts
|
|
477
|
-
import { readFileSync as
|
|
626
|
+
import { readFileSync as readFileSync5 } from "fs";
|
|
478
627
|
function registerSpecCommands(program2) {
|
|
479
628
|
const spec = program2.command("spec").description("Manage spec documents");
|
|
480
629
|
spec.command("list").description("List spec documents").option("-t, --project-id <projectId>", "List spec for a specific root project").action(async (opts) => {
|
|
@@ -515,7 +664,7 @@ ${contexts.length} spec(s)`);
|
|
|
515
664
|
const client = getClient();
|
|
516
665
|
let text = opts.text;
|
|
517
666
|
if (opts.file) {
|
|
518
|
-
text =
|
|
667
|
+
text = readFileSync5(opts.file, "utf-8");
|
|
519
668
|
}
|
|
520
669
|
if (!(text || opts.name)) {
|
|
521
670
|
console.error("Provide --text, --file, or --name to update.");
|
|
@@ -599,8 +748,9 @@ ${preview}`);
|
|
|
599
748
|
}
|
|
600
749
|
|
|
601
750
|
// src/index.ts
|
|
602
|
-
var
|
|
603
|
-
var pkg = JSON.parse(
|
|
751
|
+
var __dirname2 = dirname3(fileURLToPath2(import.meta.url));
|
|
752
|
+
var pkg = JSON.parse(readFileSync6(resolve3(__dirname2, "../package.json"), "utf-8"));
|
|
753
|
+
updateNotifier({ pkg }).notify();
|
|
604
754
|
var program = new Command();
|
|
605
755
|
program.name("prim").description("CLI for managing Primitive specs and contexts").version(pkg.version);
|
|
606
756
|
registerAuthCommands(program);
|
|
@@ -608,6 +758,7 @@ registerContextCommands(program);
|
|
|
608
758
|
registerSpecCommands(program);
|
|
609
759
|
registerProjectCommands(program);
|
|
610
760
|
registerHooksCommands(program);
|
|
761
|
+
registerSkillCommands(program);
|
|
611
762
|
process.on("unhandledRejection", (err) => {
|
|
612
763
|
const msg = err instanceof Error ? err.message : String(err);
|
|
613
764
|
console.error(msg);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@primitive.ai/prim",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.13",
|
|
4
4
|
"description": "CLI for managing Primitive specs, contexts, and git hooks",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -35,7 +35,8 @@
|
|
|
35
35
|
"files": [
|
|
36
36
|
"dist",
|
|
37
37
|
"LICENSE",
|
|
38
|
-
"README.md"
|
|
38
|
+
"README.md",
|
|
39
|
+
"SKILL.md"
|
|
39
40
|
],
|
|
40
41
|
"scripts": {
|
|
41
42
|
"build": "tsup src/index.ts src/hooks/pre-commit.ts --format esm --clean",
|
|
@@ -51,11 +52,15 @@
|
|
|
51
52
|
"test:coverage": "vitest run --coverage"
|
|
52
53
|
},
|
|
53
54
|
"dependencies": {
|
|
54
|
-
"commander": "^12.1.0"
|
|
55
|
+
"commander": "^12.1.0",
|
|
56
|
+
"diff": "^5.2.0",
|
|
57
|
+
"update-notifier": "^7.3.1"
|
|
55
58
|
},
|
|
56
59
|
"devDependencies": {
|
|
57
60
|
"@biomejs/biome": "^1.9.0",
|
|
61
|
+
"@types/diff": "^5.2.0",
|
|
58
62
|
"@types/node": "^25.5.0",
|
|
63
|
+
"@types/update-notifier": "^6.0.8",
|
|
59
64
|
"@vitest/coverage-v8": "^3.1.0",
|
|
60
65
|
"tsup": "^8.0.0",
|
|
61
66
|
"typescript": "^5.5.0",
|