origem 0.2.2 → 0.3.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 +13 -0
- package/dist/index.js +170 -12
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -27,6 +27,19 @@ session hooks. Running it again is how you sync.
|
|
|
27
27
|
|
|
28
28
|
`origem help` lists every option.
|
|
29
29
|
|
|
30
|
+
## Slash commands in Claude Code
|
|
31
|
+
|
|
32
|
+
`connect` also installs these, so you do not have to remember the CLI:
|
|
33
|
+
|
|
34
|
+
| Command | What it does |
|
|
35
|
+
|---|---|
|
|
36
|
+
| `/origem:sync` | Pull the newest harness revision |
|
|
37
|
+
| `/origem:spec` | Open specs and what is missing to close a stage |
|
|
38
|
+
| `/origem:doctor` | Check drift between harness and repo |
|
|
39
|
+
| `/origem:propose` | Suggest a permanent change |
|
|
40
|
+
| `/origem:capture` | Turn this session's decisions into proposals |
|
|
41
|
+
| `/origem:bootstrap` | Propose a whole harness from the repo |
|
|
42
|
+
|
|
30
43
|
## What a harness is
|
|
31
44
|
|
|
32
45
|
A complete Claude Code setup: instructions, rules, boundaries, memory, skills,
|
package/dist/index.js
CHANGED
|
@@ -1402,6 +1402,20 @@ the project and you edit them normally.
|
|
|
1402
1402
|
| \`npx origem doctor\` | Compares the harness against this repository and reports what drifted. |
|
|
1403
1403
|
| \`npx origem bootstrap\` | Reads the repository and proposes a whole harness (for a project without one). |
|
|
1404
1404
|
|
|
1405
|
+
## Slash commands available in this repository
|
|
1406
|
+
|
|
1407
|
+
The user can type these directly \u2014 prefer pointing them at one instead of
|
|
1408
|
+
explaining a raw \`npx\` invocation:
|
|
1409
|
+
|
|
1410
|
+
| Command | What it does |
|
|
1411
|
+
|---|---|
|
|
1412
|
+
| \`/origem:sync\` | Pull the newest harness revision |
|
|
1413
|
+
| \`/origem:spec\` | Open specs and what is missing to close a stage |
|
|
1414
|
+
| \`/origem:doctor\` | Check drift between harness and repo |
|
|
1415
|
+
| \`/origem:propose\` | Suggest a permanent change |
|
|
1416
|
+
| \`/origem:capture\` | Turn this session's decisions into proposals |
|
|
1417
|
+
| \`/origem:bootstrap\` | Propose a whole harness from the repo |
|
|
1418
|
+
|
|
1405
1419
|
## At the start of a task
|
|
1406
1420
|
|
|
1407
1421
|
Run \`npx origem spec\`. If there is an open spec related to what the user asked, use its
|
|
@@ -1465,6 +1479,148 @@ function installOrigemSkill(baseDir, lock) {
|
|
|
1465
1479
|
ok(`Origem skill installed at ${SKILL_PATH}`);
|
|
1466
1480
|
}
|
|
1467
1481
|
|
|
1482
|
+
// src/commands-install.ts
|
|
1483
|
+
import { existsSync as existsSync8, mkdirSync as mkdirSync6, readFileSync as readFileSync6, writeFileSync as writeFileSync6 } from "node:fs";
|
|
1484
|
+
import { dirname as dirname6, join as join7 } from "node:path";
|
|
1485
|
+
var COMMANDS = [
|
|
1486
|
+
{
|
|
1487
|
+
file: "sync.md",
|
|
1488
|
+
content: `---
|
|
1489
|
+
description: Pull the newest harness revision from Origem and rewrite the local config files
|
|
1490
|
+
allowed-tools: Bash(npx origem:*)
|
|
1491
|
+
---
|
|
1492
|
+
|
|
1493
|
+
Run \`npx origem status\` first and show me what would change.
|
|
1494
|
+
|
|
1495
|
+
If any managed block was edited locally, stop and tell me before applying \u2014 the
|
|
1496
|
+
apply overwrites it.
|
|
1497
|
+
|
|
1498
|
+
Otherwise run \`npx origem connect\` and report what changed.
|
|
1499
|
+
`
|
|
1500
|
+
},
|
|
1501
|
+
{
|
|
1502
|
+
file: "spec.md",
|
|
1503
|
+
content: `---
|
|
1504
|
+
description: Show the open specs and which stage each one is in
|
|
1505
|
+
allowed-tools: Bash(npx origem:*)
|
|
1506
|
+
---
|
|
1507
|
+
|
|
1508
|
+
Run \`npx origem spec\`.
|
|
1509
|
+
|
|
1510
|
+
Then tell me, in one short paragraph: which spec is closest to done, what is
|
|
1511
|
+
missing to close its current stage, and whether anything we did in this session
|
|
1512
|
+
already satisfies one of the acceptance criteria.
|
|
1513
|
+
`
|
|
1514
|
+
},
|
|
1515
|
+
{
|
|
1516
|
+
file: "doctor.md",
|
|
1517
|
+
content: `---
|
|
1518
|
+
description: Check whether the harness still matches this repository (drift)
|
|
1519
|
+
allowed-tools: Bash(npx origem:*)
|
|
1520
|
+
---
|
|
1521
|
+
|
|
1522
|
+
Run \`npx origem doctor\`.
|
|
1523
|
+
|
|
1524
|
+
For each finding, say whether fixing it is worth it and what the fix would be.
|
|
1525
|
+
Do not change anything yet \u2014 drift is fixed in the harness, not in local files.
|
|
1526
|
+
If I agree with a fix, use \`/origem:propose\` to send it.
|
|
1527
|
+
`
|
|
1528
|
+
},
|
|
1529
|
+
{
|
|
1530
|
+
file: "propose.md",
|
|
1531
|
+
content: `---
|
|
1532
|
+
description: Suggest a permanent change to the harness (stays pending until accepted)
|
|
1533
|
+
allowed-tools: Bash(npx origem:*)
|
|
1534
|
+
---
|
|
1535
|
+
|
|
1536
|
+
Propose this change to the harness: $ARGUMENTS
|
|
1537
|
+
|
|
1538
|
+
Before proposing:
|
|
1539
|
+
1. Read the current harness pieces so you do not propose something already there.
|
|
1540
|
+
2. Pick the right compartment \u2014 \`rule\`, \`boundary\`, \`memory\`, \`knowledge\`,
|
|
1541
|
+
\`skill\`, \`agent\`, \`prompt\`, \`instruction\`, \`harness\`, \`mcp\`, \`integration\`.
|
|
1542
|
+
3. Write \`--why\` citing what happened in THIS session. That sentence is what
|
|
1543
|
+
the reviewer reads to decide \u2014 a generic justification gets rejected.
|
|
1544
|
+
|
|
1545
|
+
Then run \`npx origem propose\` with \`--type\`, \`--title\`, \`--content\` and \`--why\`.
|
|
1546
|
+
|
|
1547
|
+
Propose one thing only. If there are two changes, ask me which one first.
|
|
1548
|
+
`
|
|
1549
|
+
},
|
|
1550
|
+
{
|
|
1551
|
+
file: "capture.md",
|
|
1552
|
+
content: `---
|
|
1553
|
+
description: Turn what we just decided in this conversation into a harness proposal
|
|
1554
|
+
allowed-tools: Bash(npx origem:*)
|
|
1555
|
+
---
|
|
1556
|
+
|
|
1557
|
+
Look back over this session and find decisions that should hold in FUTURE
|
|
1558
|
+
sessions \u2014 a convention we settled on, a correction I made more than once, a
|
|
1559
|
+
command that turned out to be wrong.
|
|
1560
|
+
|
|
1561
|
+
Ignore anything specific to the task at hand. A harness piece is only worth it
|
|
1562
|
+
if it would still be true next week.
|
|
1563
|
+
|
|
1564
|
+
List what you found, one line each, and ask me which to send. For each one I
|
|
1565
|
+
approve, run \`npx origem propose\` with \`--why\` citing the moment in this
|
|
1566
|
+
session that justifies it.
|
|
1567
|
+
|
|
1568
|
+
If you find nothing that durable, say so \u2014 proposing noise is worse than
|
|
1569
|
+
proposing nothing.
|
|
1570
|
+
`
|
|
1571
|
+
},
|
|
1572
|
+
{
|
|
1573
|
+
file: "bootstrap.md",
|
|
1574
|
+
content: `---
|
|
1575
|
+
description: Read this repository and propose a whole harness for it
|
|
1576
|
+
allowed-tools: Bash(npx origem:*)
|
|
1577
|
+
---
|
|
1578
|
+
|
|
1579
|
+
Run \`npx origem bootstrap --dry-run\` and show me what it would propose.
|
|
1580
|
+
|
|
1581
|
+
Tell me what it got right and what is missing \u2014 it reads \`package.json\`,
|
|
1582
|
+
\`Makefile\`, the lockfile, docs and the commit history, so it will miss anything
|
|
1583
|
+
that only lives in someone's head.
|
|
1584
|
+
|
|
1585
|
+
If I approve, run \`npx origem bootstrap --yes\`, then remind me the pieces are
|
|
1586
|
+
PROPOSALS: nothing enters the harness until I accept the batch in Origem.
|
|
1587
|
+
`
|
|
1588
|
+
}
|
|
1589
|
+
];
|
|
1590
|
+
var COMMANDS_DIR = join7(".claude", "commands", "origem");
|
|
1591
|
+
function installOrigemCommands(baseDir, lock) {
|
|
1592
|
+
const fromHarness = new Set(
|
|
1593
|
+
(lock?.files ?? []).map((f) => f.path.replace(/\\/g, "/"))
|
|
1594
|
+
);
|
|
1595
|
+
let written = 0;
|
|
1596
|
+
let skipped = 0;
|
|
1597
|
+
for (const cmd of COMMANDS) {
|
|
1598
|
+
const rel = join7(COMMANDS_DIR, cmd.file);
|
|
1599
|
+
if (fromHarness.has(rel.replace(/\\/g, "/"))) {
|
|
1600
|
+
skipped++;
|
|
1601
|
+
continue;
|
|
1602
|
+
}
|
|
1603
|
+
const abs = join7(baseDir, rel);
|
|
1604
|
+
if (existsSync8(abs) && readFileSync6(abs, "utf8") === cmd.content) continue;
|
|
1605
|
+
mkdirSync6(dirname6(abs), { recursive: true });
|
|
1606
|
+
writeFileSync6(abs, cmd.content, "utf8");
|
|
1607
|
+
written++;
|
|
1608
|
+
}
|
|
1609
|
+
if (written > 0) {
|
|
1610
|
+
ok(`${written} slash command(s) installed in ${COMMANDS_DIR}`);
|
|
1611
|
+
info(
|
|
1612
|
+
color.dim(
|
|
1613
|
+
` /origem:sync /origem:spec /origem:doctor /origem:propose /origem:capture /origem:bootstrap`
|
|
1614
|
+
)
|
|
1615
|
+
);
|
|
1616
|
+
} else {
|
|
1617
|
+
info(color.dim(`slash commands already up to date in ${COMMANDS_DIR}`));
|
|
1618
|
+
}
|
|
1619
|
+
if (skipped > 0) {
|
|
1620
|
+
info(color.dim(` ${skipped} kept as the harness provides them`));
|
|
1621
|
+
}
|
|
1622
|
+
}
|
|
1623
|
+
|
|
1468
1624
|
// src/connect.ts
|
|
1469
1625
|
var CALLBACK_TIMEOUT_MS = 5 * 60 * 1e3;
|
|
1470
1626
|
function base64url(buf) {
|
|
@@ -1662,7 +1818,9 @@ async function runConnect(opts) {
|
|
|
1662
1818
|
dryRun: false,
|
|
1663
1819
|
yes: opts.yes
|
|
1664
1820
|
});
|
|
1665
|
-
|
|
1821
|
+
const lock = readLock(process.cwd());
|
|
1822
|
+
installOrigemSkill(process.cwd(), lock);
|
|
1823
|
+
installOrigemCommands(process.cwd(), lock);
|
|
1666
1824
|
if (opts.noHooks) {
|
|
1667
1825
|
info(color.dim("hooks skipped (--no-hooks): the session will not report turns to Origem."));
|
|
1668
1826
|
} else {
|
|
@@ -1682,7 +1840,7 @@ async function runConnect(opts) {
|
|
|
1682
1840
|
}
|
|
1683
1841
|
|
|
1684
1842
|
// src/propose.ts
|
|
1685
|
-
import { readFileSync as
|
|
1843
|
+
import { readFileSync as readFileSync7 } from "node:fs";
|
|
1686
1844
|
var PROPOSABLE = [
|
|
1687
1845
|
"instruction",
|
|
1688
1846
|
"rule",
|
|
@@ -1703,7 +1861,7 @@ async function runPropose(opts) {
|
|
|
1703
1861
|
if (!opts.rationale?.trim()) {
|
|
1704
1862
|
throw new Error("--why is required: say why you are proposing it \u2014 that is what the reviewer reads.");
|
|
1705
1863
|
}
|
|
1706
|
-
const content = opts.file ?
|
|
1864
|
+
const content = opts.file ? readFileSync7(opts.file, "utf8") : opts.content ?? "";
|
|
1707
1865
|
if (!content.trim()) {
|
|
1708
1866
|
throw new Error("Empty content. Pass --content <text> or --file <path>.");
|
|
1709
1867
|
}
|
|
@@ -1771,8 +1929,8 @@ ${specs.length} open spec(s). When you close a stage, it is reported by \`origem
|
|
|
1771
1929
|
|
|
1772
1930
|
// src/repo.ts
|
|
1773
1931
|
import { execFileSync } from "node:child_process";
|
|
1774
|
-
import { existsSync as
|
|
1775
|
-
import { basename as basename2, extname, join as
|
|
1932
|
+
import { existsSync as existsSync9, readFileSync as readFileSync8, readdirSync as readdirSync2, statSync } from "node:fs";
|
|
1933
|
+
import { basename as basename2, extname, join as join8, relative } from "node:path";
|
|
1776
1934
|
var SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
1777
1935
|
"node_modules",
|
|
1778
1936
|
".git",
|
|
@@ -1793,7 +1951,7 @@ var DOC_EXT = /* @__PURE__ */ new Set([".md", ".mdx"]);
|
|
|
1793
1951
|
var MAX_FILE_CHARS = 2e4;
|
|
1794
1952
|
function read(path) {
|
|
1795
1953
|
try {
|
|
1796
|
-
return
|
|
1954
|
+
return readFileSync8(path, "utf8");
|
|
1797
1955
|
} catch {
|
|
1798
1956
|
return null;
|
|
1799
1957
|
}
|
|
@@ -1809,7 +1967,7 @@ function walk(dir, base, depth = 0, out = []) {
|
|
|
1809
1967
|
for (const name of entries) {
|
|
1810
1968
|
if (name.startsWith(".") && name !== ".github") continue;
|
|
1811
1969
|
if (SKIP_DIRS.has(name)) continue;
|
|
1812
|
-
const full =
|
|
1970
|
+
const full = join8(dir, name);
|
|
1813
1971
|
let st;
|
|
1814
1972
|
try {
|
|
1815
1973
|
st = statSync(full);
|
|
@@ -1825,7 +1983,7 @@ function readRepo(baseDir) {
|
|
|
1825
1983
|
const files = walk(baseDir, baseDir);
|
|
1826
1984
|
const scripts = [];
|
|
1827
1985
|
const languages = /* @__PURE__ */ new Set();
|
|
1828
|
-
const pkgRaw = read(
|
|
1986
|
+
const pkgRaw = read(join8(baseDir, "package.json"));
|
|
1829
1987
|
if (pkgRaw) {
|
|
1830
1988
|
try {
|
|
1831
1989
|
const pkg = JSON.parse(pkgRaw);
|
|
@@ -1835,14 +1993,14 @@ function readRepo(baseDir) {
|
|
|
1835
1993
|
} catch {
|
|
1836
1994
|
}
|
|
1837
1995
|
}
|
|
1838
|
-
const makefile = read(
|
|
1996
|
+
const makefile = read(join8(baseDir, "Makefile"));
|
|
1839
1997
|
if (makefile) {
|
|
1840
1998
|
for (const line of makefile.split("\n")) {
|
|
1841
1999
|
const m = /^([a-zA-Z][\w-]*):(?!=)/.exec(line);
|
|
1842
2000
|
if (m) scripts.push({ name: m[1], command: `make ${m[1]}`, from: "Makefile" });
|
|
1843
2001
|
}
|
|
1844
2002
|
}
|
|
1845
|
-
const pyproject = read(
|
|
2003
|
+
const pyproject = read(join8(baseDir, "pyproject.toml"));
|
|
1846
2004
|
if (pyproject) languages.add("python");
|
|
1847
2005
|
for (const f of files) {
|
|
1848
2006
|
const ext = extname(f);
|
|
@@ -1855,8 +2013,8 @@ function readRepo(baseDir) {
|
|
|
1855
2013
|
else if (ext === ".java") languages.add("java");
|
|
1856
2014
|
else if (ext === ".cs") languages.add("csharp");
|
|
1857
2015
|
}
|
|
1858
|
-
const packageManager =
|
|
1859
|
-
const docs = files.filter((f) => DOC_EXT.has(extname(f)) && (!f.includes("/") || f.startsWith("docs/"))).slice(0, 12).map((f) => ({ path: f, content: (read(
|
|
2016
|
+
const packageManager = existsSync9(join8(baseDir, "pnpm-lock.yaml")) ? "pnpm" : existsSync9(join8(baseDir, "yarn.lock")) ? "yarn" : existsSync9(join8(baseDir, "bun.lockb")) ? "bun" : existsSync9(join8(baseDir, "package-lock.json")) ? "npm" : null;
|
|
2017
|
+
const docs = files.filter((f) => DOC_EXT.has(extname(f)) && (!f.includes("/") || f.startsWith("docs/"))).slice(0, 12).map((f) => ({ path: f, content: (read(join8(baseDir, f)) ?? "").slice(0, MAX_FILE_CHARS) })).filter((d) => d.content.trim());
|
|
1860
2018
|
return {
|
|
1861
2019
|
name: basename2(baseDir),
|
|
1862
2020
|
scripts,
|
package/package.json
CHANGED