opennori 0.1.3 → 0.1.4
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 +5 -0
- package/package.json +1 -1
- package/src/cli.js +101 -3
package/README.md
CHANGED
|
@@ -28,6 +28,9 @@ judgment.
|
|
|
28
28
|
npx opennori
|
|
29
29
|
```
|
|
30
30
|
|
|
31
|
+
It shows a short project setup preview and asks before writing `.opennori/` or the OpenNori Skill
|
|
32
|
+
Pack. Agents and CI can add `--json` for the deterministic machine-readable protocol.
|
|
33
|
+
|
|
31
34
|
For a project install:
|
|
32
35
|
|
|
33
36
|
```bash
|
|
@@ -82,6 +85,8 @@ language requests to the deterministic CLI state layer.
|
|
|
82
85
|
- `bootstrap` gives agents one short entry for readiness checks and first-time preview. Lower-level
|
|
83
86
|
`install`, `upgrade`, and `uninstall` still support preview-first workflows; destructive writes
|
|
84
87
|
require explicit confirmation.
|
|
88
|
+
- In a human terminal, `npx opennori` is interactive. With `--json` or non-interactive stdio it
|
|
89
|
+
returns structured JSON for agents and automation.
|
|
85
90
|
- `doctor` reports whether project state is `ready`, `needs-action`, or `broken`, with recovery
|
|
86
91
|
actions.
|
|
87
92
|
- Nori Profile records required Skills, preferred stacks, avoided tools, and install policy without
|
package/package.json
CHANGED
package/src/cli.js
CHANGED
|
@@ -375,6 +375,10 @@ function printJson(payload) {
|
|
|
375
375
|
console.log(JSON.stringify(payload, null, 2));
|
|
376
376
|
}
|
|
377
377
|
|
|
378
|
+
function printText(line = "") {
|
|
379
|
+
process.stdout.write(`${line}\n`);
|
|
380
|
+
}
|
|
381
|
+
|
|
378
382
|
function argValue(args, name, fallback = undefined) {
|
|
379
383
|
const index = args.indexOf(name);
|
|
380
384
|
if (index === -1 || index + 1 >= args.length) return fallback;
|
|
@@ -385,6 +389,14 @@ function hasFlag(args, name) {
|
|
|
385
389
|
return args.includes(name);
|
|
386
390
|
}
|
|
387
391
|
|
|
392
|
+
function wantsJson(args) {
|
|
393
|
+
return hasFlag(args, "--json");
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
function isInteractive(args) {
|
|
397
|
+
return !wantsJson(args) && process.stdin.isTTY && process.stdout.isTTY;
|
|
398
|
+
}
|
|
399
|
+
|
|
388
400
|
const CLI_NAME = "opennori";
|
|
389
401
|
const TOP_LEVEL_USAGE = `${CLI_NAME} <bootstrap|doctor|install|upgrade|uninstall|brainstorm|draft|init|list|check|approve|criterion|profile|resume|next|evidence|evaluate|status|report|context|changes|archive|skill>`;
|
|
390
402
|
|
|
@@ -419,6 +431,94 @@ function usageFor(args) {
|
|
|
419
431
|
return TOP_LEVEL_USAGE;
|
|
420
432
|
}
|
|
421
433
|
|
|
434
|
+
function describeBootstrapAction(action) {
|
|
435
|
+
if (action.action === "create") return `create ${action.path}`;
|
|
436
|
+
if (action.action === "skip") return `keep existing ${action.path}`;
|
|
437
|
+
if (action.action === "exists") return `already exists ${action.path}`;
|
|
438
|
+
if (action.action === "update") return `update ${action.path}`;
|
|
439
|
+
if (action.action === "overwrite") return `overwrite ${action.path}`;
|
|
440
|
+
return `${action.action} ${action.path}`;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
function printBootstrapPreview(payload) {
|
|
444
|
+
const data = payload.data;
|
|
445
|
+
printText("");
|
|
446
|
+
printText("OpenNori project setup");
|
|
447
|
+
printText(`Project: ${data.root}`);
|
|
448
|
+
printText("");
|
|
449
|
+
|
|
450
|
+
if (data.status === "ready") {
|
|
451
|
+
printText("OpenNori is already ready in this project.");
|
|
452
|
+
printText("Next: tell your agent the goal and ask it to use OpenNori.");
|
|
453
|
+
return;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
printText("This will prepare OpenNori for this project:");
|
|
457
|
+
for (const action of data.install_plan.actions.filter((item) => item.would_write).slice(0, 8)) {
|
|
458
|
+
printText(`- ${describeBootstrapAction(action)}`);
|
|
459
|
+
}
|
|
460
|
+
const remaining = data.install_plan.summary.would_write - Math.min(data.install_plan.summary.would_write, 8);
|
|
461
|
+
if (remaining > 0) printText(`- plus ${remaining} more OpenNori project assets`);
|
|
462
|
+
printText("");
|
|
463
|
+
printText("No files have been written yet.");
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
function printBootstrapResult(payload) {
|
|
467
|
+
const data = payload.data;
|
|
468
|
+
printText("");
|
|
469
|
+
if (data.status === "installed") {
|
|
470
|
+
printText("OpenNori installed.");
|
|
471
|
+
printText(`Created or refreshed ${data.install_plan.summary.will_write} project assets.`);
|
|
472
|
+
printText("Next: tell your agent the goal and ask it to use OpenNori.");
|
|
473
|
+
return;
|
|
474
|
+
}
|
|
475
|
+
if (data.status === "ready") {
|
|
476
|
+
printText("OpenNori is ready.");
|
|
477
|
+
printText("Next: tell your agent the goal and ask it to use OpenNori.");
|
|
478
|
+
return;
|
|
479
|
+
}
|
|
480
|
+
printText(data.next || "OpenNori bootstrap finished.");
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
async function promptConfirm(message) {
|
|
484
|
+
process.stdout.write(`${message} [y/N] `);
|
|
485
|
+
return new Promise((resolve) => {
|
|
486
|
+
process.stdin.setEncoding("utf8");
|
|
487
|
+
process.stdin.once("data", (chunk) => {
|
|
488
|
+
process.stdin.pause();
|
|
489
|
+
resolve(/^y(es)?$/i.test(String(chunk).trim()));
|
|
490
|
+
});
|
|
491
|
+
});
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
async function runBootstrap(args) {
|
|
495
|
+
const root = resolveRoot(args);
|
|
496
|
+
const confirmed = hasFlag(args, "--confirm");
|
|
497
|
+
|
|
498
|
+
if (!isInteractive(args)) {
|
|
499
|
+
printJson(bootstrap(root, { confirmed }));
|
|
500
|
+
return;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
if (confirmed) {
|
|
504
|
+
printBootstrapResult(bootstrap(root, { confirmed: true }));
|
|
505
|
+
return;
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
const preview = bootstrap(root, { confirmed: false });
|
|
509
|
+
printBootstrapPreview(preview);
|
|
510
|
+
if (preview.data.status === "ready") return;
|
|
511
|
+
|
|
512
|
+
const shouldInstall = await promptConfirm("Install OpenNori here?");
|
|
513
|
+
if (!shouldInstall) {
|
|
514
|
+
printText("");
|
|
515
|
+
printText("No changes made.");
|
|
516
|
+
return;
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
printBootstrapResult(bootstrap(root, { confirmed: true }));
|
|
520
|
+
}
|
|
521
|
+
|
|
422
522
|
function argValues(args, name) {
|
|
423
523
|
const values = [];
|
|
424
524
|
for (let index = 0; index < args.length; index += 1) {
|
|
@@ -1495,9 +1595,7 @@ export async function main(args) {
|
|
|
1495
1595
|
}
|
|
1496
1596
|
|
|
1497
1597
|
if (command === "bootstrap") {
|
|
1498
|
-
|
|
1499
|
-
const confirmed = hasFlag(args, "--confirm");
|
|
1500
|
-
printJson(bootstrap(root, { confirmed }));
|
|
1598
|
+
await runBootstrap(args);
|
|
1501
1599
|
return;
|
|
1502
1600
|
}
|
|
1503
1601
|
|