@opennous/cli 0.7.4 → 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 CHANGED
@@ -1,17 +1,28 @@
1
1
  {
2
2
  "name": "@opennous/cli",
3
- "version": "0.7.4",
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": ["src", "README.md"],
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": ["nous", "crm", "cli", "agents", "memory", "observability", "stripe"],
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
@@ -961,6 +961,42 @@ function registerMcp(apiUrl) {
961
961
  return `Wrote ${path} — your agent will pick up Nous from it.`;
962
962
  }
963
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
+
964
1000
  // nous init — the one-line "get started": sign in, register the MCP, hand off to the agent.
965
1001
  // This is the command the install script and the marketing site point to.
966
1002
  program
@@ -980,6 +1016,14 @@ program
980
1016
  console.log("\nCouldn't register the MCP automatically. Add it with:");
981
1017
  console.log(` claude mcp add nous -- ${MCP_CMD} ${MCP_ARGS.join(" ")}`);
982
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 */ }
983
1027
  }
984
1028
 
985
1029
  console.log("\nYou're set. Tell your agent:\n");