@opennous/cli 0.7.2 → 0.7.5
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/package.json +14 -3
- package/src/hooks/icp-sync-reminder.mjs +33 -0
- package/src/index.js +55 -7
package/package.json
CHANGED
|
@@ -1,17 +1,28 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opennous/cli",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.5",
|
|
4
4
|
"description": "Nous CLI — set up Nous, connect it to your coding agent, and read the Context API from your terminal",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"nous": "src/index.js"
|
|
8
8
|
},
|
|
9
|
-
"files": [
|
|
9
|
+
"files": [
|
|
10
|
+
"src",
|
|
11
|
+
"README.md"
|
|
12
|
+
],
|
|
10
13
|
"scripts": {
|
|
11
14
|
"dev": "node src/index.js",
|
|
12
15
|
"start": "node src/index.js"
|
|
13
16
|
},
|
|
14
|
-
"keywords": [
|
|
17
|
+
"keywords": [
|
|
18
|
+
"nous",
|
|
19
|
+
"crm",
|
|
20
|
+
"cli",
|
|
21
|
+
"agents",
|
|
22
|
+
"memory",
|
|
23
|
+
"observability",
|
|
24
|
+
"stripe"
|
|
25
|
+
],
|
|
15
26
|
"license": "Apache-2.0",
|
|
16
27
|
"repository": {
|
|
17
28
|
"type": "git",
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Nous ICP auto-sync reminder (installed by `npx @opennous/cli init`).
|
|
3
|
+
//
|
|
4
|
+
// A Claude Code PostToolUse hook. After the agent edits a Nous context file, it
|
|
5
|
+
// reminds the agent that the file is INERT until synced: the app, the ICP tab, and
|
|
6
|
+
// the ICP scoring model read the Nous GRAPH, not the repo file, so an edit does
|
|
7
|
+
// nothing until mcp__nous__sync_icp runs. Standalone Node (no bash/jq) so it behaves
|
|
8
|
+
// identically on Windows, macOS, and Linux.
|
|
9
|
+
|
|
10
|
+
let raw = "";
|
|
11
|
+
process.stdin.on("data", (c) => { raw += c; });
|
|
12
|
+
process.stdin.on("end", () => {
|
|
13
|
+
try {
|
|
14
|
+
const j = JSON.parse(raw || "{}");
|
|
15
|
+
const f = (j && j.tool_input && j.tool_input.file_path) || (j && j.tool_response && j.tool_response.filePath) || "";
|
|
16
|
+
const norm = String(f).replace(/\\/g, "/"); // normalize Windows separators
|
|
17
|
+
if (!/\.md$/i.test(norm) || !/\/context\//i.test(norm)) return;
|
|
18
|
+
|
|
19
|
+
// Report the path relative to the project so the agent passes the right source_path.
|
|
20
|
+
const proj = (process.env.CLAUDE_PROJECT_DIR || "").replace(/\\/g, "/");
|
|
21
|
+
let rel = norm;
|
|
22
|
+
if (proj && norm.toLowerCase().startsWith(proj.toLowerCase())) {
|
|
23
|
+
rel = norm.slice(proj.length).replace(/^\/+/, "");
|
|
24
|
+
}
|
|
25
|
+
const base = norm.split("/").pop().toLowerCase();
|
|
26
|
+
|
|
27
|
+
const ctx = base === "icp.md"
|
|
28
|
+
? `You just edited ${rel}, your Nous ICP file. It is INERT until synced: the app, the ICP tab, and the ICP scoring model read the Nous graph, not this file. Call the mcp__nous__sync_icp tool THIS TURN with section "ICP" and source_path "${rel}", passing the file body as the section content, so the graph and the scoring model rebuild from your edit. Do not end the turn with the ICP file edited but unsynced.`
|
|
29
|
+
: `You edited a Nous context file (${rel}). It stays inert until synced into the Nous graph. If it is an ICP, positioning, pricing, competitors, or market file, call mcp__nous__sync_icp this turn with the matching section and its source_path so the graph updates.`;
|
|
30
|
+
|
|
31
|
+
process.stdout.write(JSON.stringify({ hookSpecificOutput: { hookEventName: "PostToolUse", additionalContext: ctx } }));
|
|
32
|
+
} catch { /* never block a tool call */ }
|
|
33
|
+
});
|
package/src/index.js
CHANGED
|
@@ -32,12 +32,16 @@ function openBrowser(url) {
|
|
|
32
32
|
if (process.platform === "darwin") {
|
|
33
33
|
cmd = "open"; args = [url];
|
|
34
34
|
} else if (process.platform === "win32") {
|
|
35
|
-
// cmd.exe /c start "" "<url>".
|
|
36
|
-
//
|
|
37
|
-
//
|
|
38
|
-
//
|
|
35
|
+
// cmd.exe /c start "" "<url>". `start` reads a lone quoted arg as the window
|
|
36
|
+
// TITLE, so it needs an explicit empty title before the URL — otherwise it just
|
|
37
|
+
// opens a shell titled with the URL and never launches the browser.
|
|
38
|
+
// CRITICAL: the title must be a LITERAL `""` string, not JS's zero-length "".
|
|
39
|
+
// With windowsVerbatimArguments the args are joined by spaces as-is, so a
|
|
40
|
+
// zero-length string collapses to nothing and the title is lost (the URL then
|
|
41
|
+
// becomes the title). Verbatim also keeps our quotes, so an & in the auth URL's
|
|
42
|
+
// query string stays inside them and isn't parsed by cmd as a command separator.
|
|
39
43
|
cmd = process.env.ComSpec || "cmd.exe";
|
|
40
|
-
args = ["/c", "start", "", `"${url}"`];
|
|
44
|
+
args = ["/c", "start", '""', `"${url}"`];
|
|
41
45
|
opts.windowsVerbatimArguments = true;
|
|
42
46
|
} else {
|
|
43
47
|
cmd = "xdg-open"; args = [url];
|
|
@@ -957,6 +961,42 @@ function registerMcp(apiUrl) {
|
|
|
957
961
|
return `Wrote ${path} — your agent will pick up Nous from it.`;
|
|
958
962
|
}
|
|
959
963
|
|
|
964
|
+
// Install the ICP auto-sync hook into the project's Claude Code config. Once in place,
|
|
965
|
+
// editing a Nous context file (context/**/icp.md, etc.) reminds the agent to call
|
|
966
|
+
// sync_icp that turn — because an edited ICP file is INERT until synced into the graph.
|
|
967
|
+
// Copies our packaged hook script into .claude/hooks/ and merges the PostToolUse entry
|
|
968
|
+
// into .claude/settings.json WITHOUT clobbering any existing hooks or settings.
|
|
969
|
+
// Returns true if it was already installed. Uses the exec (args) form so there's no
|
|
970
|
+
// shell/quoting difference between Windows PowerShell and macOS/Linux bash.
|
|
971
|
+
function installIcpSyncHook() {
|
|
972
|
+
const claudeDir = join(process.cwd(), ".claude");
|
|
973
|
+
const hooksDir = join(claudeDir, "hooks");
|
|
974
|
+
const scriptPath = join(hooksDir, "icp-sync-reminder.mjs");
|
|
975
|
+
const settingsPath = join(claudeDir, "settings.json");
|
|
976
|
+
|
|
977
|
+
const src = readFileSync(new URL("./hooks/icp-sync-reminder.mjs", import.meta.url), "utf8");
|
|
978
|
+
mkdirSync(hooksDir, { recursive: true });
|
|
979
|
+
writeFileSync(scriptPath, src);
|
|
980
|
+
|
|
981
|
+
let settings = {};
|
|
982
|
+
if (existsSync(settingsPath)) {
|
|
983
|
+
try { settings = JSON.parse(readFileSync(settingsPath, "utf8")) || {}; } catch { settings = {}; }
|
|
984
|
+
}
|
|
985
|
+
settings.hooks = settings.hooks || {};
|
|
986
|
+
const post = settings.hooks.PostToolUse = settings.hooks.PostToolUse || [];
|
|
987
|
+
const already = post.some((e) => (e.hooks || []).some((h) =>
|
|
988
|
+
(Array.isArray(h.args) && h.args.some((a) => String(a).includes("icp-sync-reminder"))) ||
|
|
989
|
+
String(h.command || "").includes("icp-sync-reminder")));
|
|
990
|
+
if (!already) {
|
|
991
|
+
post.push({
|
|
992
|
+
matcher: "Write|Edit|MultiEdit",
|
|
993
|
+
hooks: [{ type: "command", command: "node", args: [scriptPath], timeout: 10 }],
|
|
994
|
+
});
|
|
995
|
+
writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
996
|
+
}
|
|
997
|
+
return already;
|
|
998
|
+
}
|
|
999
|
+
|
|
960
1000
|
// nous init — the one-line "get started": sign in, register the MCP, hand off to the agent.
|
|
961
1001
|
// This is the command the install script and the marketing site point to.
|
|
962
1002
|
program
|
|
@@ -976,10 +1016,18 @@ program
|
|
|
976
1016
|
console.log("\nCouldn't register the MCP automatically. Add it with:");
|
|
977
1017
|
console.log(` claude mcp add nous -- ${MCP_CMD} ${MCP_ARGS.join(" ")}`);
|
|
978
1018
|
}
|
|
1019
|
+
|
|
1020
|
+
// Auto-sync the ICP: a Claude Code hook so editing your ICP file reminds the
|
|
1021
|
+
// agent to sync it into Nous (the file is inert until synced). Best-effort —
|
|
1022
|
+
// a convenience, never a reason to fail setup.
|
|
1023
|
+
try {
|
|
1024
|
+
const already = installIcpSyncHook();
|
|
1025
|
+
if (!already) console.log("Installed the ICP auto-sync hook — edits to your ICP file now sync to Nous automatically.");
|
|
1026
|
+
} catch { /* non-fatal */ }
|
|
979
1027
|
}
|
|
980
1028
|
|
|
981
|
-
console.log(
|
|
982
|
-
console.log("
|
|
1029
|
+
console.log("\nYou're set. Tell your agent:\n");
|
|
1030
|
+
console.log(' "set up my Nous workspace and walk me through onboarding"\n');
|
|
983
1031
|
console.log(dim("Docs: https://docs.opennous.cloud/mcp/introduction"));
|
|
984
1032
|
});
|
|
985
1033
|
|