@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 CHANGED
@@ -1,17 +1,28 @@
1
1
  {
2
2
  "name": "@opennous/cli",
3
- "version": "0.7.2",
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
@@ -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>". The empty "" is start's title argument (else a
36
- // quoted URL is taken as the window title). windowsVerbatimArguments keeps the
37
- // quotes we add, so an & in the auth URL's query string stays inside them and
38
- // isn't parsed by cmd as a command separator.
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('\nYou\'re set. Tell your agent: "set up my GTM context layer and connect it to');
982
- console.log("Nous\" — it audits your project's context, drafts what's missing, and syncs it.\n");
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