@creator-notes/cnotes 0.16.11
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/.claude-plugin/plugin.json +14 -0
- package/.mcp.json +12 -0
- package/LICENSE +21 -0
- package/README.md +303 -0
- package/dist/cn.d.ts +3 -0
- package/dist/cn.d.ts.map +1 -0
- package/dist/cn.js +124 -0
- package/dist/cn.js.map +1 -0
- package/dist/commands/auth.d.ts +10 -0
- package/dist/commands/auth.d.ts.map +1 -0
- package/dist/commands/auth.js +188 -0
- package/dist/commands/auth.js.map +1 -0
- package/dist/commands/canvas.d.ts +3 -0
- package/dist/commands/canvas.d.ts.map +1 -0
- package/dist/commands/canvas.js +1383 -0
- package/dist/commands/canvas.js.map +1 -0
- package/dist/commands/claude-hook.d.ts +28 -0
- package/dist/commands/claude-hook.d.ts.map +1 -0
- package/dist/commands/claude-hook.js +59 -0
- package/dist/commands/claude-hook.js.map +1 -0
- package/dist/commands/config.d.ts +3 -0
- package/dist/commands/config.d.ts.map +1 -0
- package/dist/commands/config.js +47 -0
- package/dist/commands/config.js.map +1 -0
- package/dist/commands/files.d.ts +3 -0
- package/dist/commands/files.d.ts.map +1 -0
- package/dist/commands/files.js +119 -0
- package/dist/commands/files.js.map +1 -0
- package/dist/commands/init.d.ts +3 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +473 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/mcp.d.ts +15 -0
- package/dist/commands/mcp.d.ts.map +1 -0
- package/dist/commands/mcp.js +118 -0
- package/dist/commands/mcp.js.map +1 -0
- package/dist/commands/memory.d.ts +3 -0
- package/dist/commands/memory.d.ts.map +1 -0
- package/dist/commands/memory.js +150 -0
- package/dist/commands/memory.js.map +1 -0
- package/dist/commands/notes.d.ts +3 -0
- package/dist/commands/notes.d.ts.map +1 -0
- package/dist/commands/notes.js +706 -0
- package/dist/commands/notes.js.map +1 -0
- package/dist/commands/operations.d.ts +18 -0
- package/dist/commands/operations.d.ts.map +1 -0
- package/dist/commands/operations.js +231 -0
- package/dist/commands/operations.js.map +1 -0
- package/dist/commands/relationships.d.ts +3 -0
- package/dist/commands/relationships.d.ts.map +1 -0
- package/dist/commands/relationships.js +94 -0
- package/dist/commands/relationships.js.map +1 -0
- package/dist/commands/schema.d.ts +12 -0
- package/dist/commands/schema.d.ts.map +1 -0
- package/dist/commands/schema.js +85 -0
- package/dist/commands/schema.js.map +1 -0
- package/dist/commands/search.d.ts +3 -0
- package/dist/commands/search.d.ts.map +1 -0
- package/dist/commands/search.js +57 -0
- package/dist/commands/search.js.map +1 -0
- package/dist/commands/theme.d.ts +3 -0
- package/dist/commands/theme.d.ts.map +1 -0
- package/dist/commands/theme.js +184 -0
- package/dist/commands/theme.js.map +1 -0
- package/dist/commands/timeline.d.ts +3 -0
- package/dist/commands/timeline.d.ts.map +1 -0
- package/dist/commands/timeline.js +97 -0
- package/dist/commands/timeline.js.map +1 -0
- package/dist/commands/types.d.ts +3 -0
- package/dist/commands/types.d.ts.map +1 -0
- package/dist/commands/types.js +139 -0
- package/dist/commands/types.js.map +1 -0
- package/dist/commands/versions.d.ts +3 -0
- package/dist/commands/versions.d.ts.map +1 -0
- package/dist/commands/versions.js +120 -0
- package/dist/commands/versions.js.map +1 -0
- package/dist/commands/workspace.d.ts +13 -0
- package/dist/commands/workspace.d.ts.map +1 -0
- package/dist/commands/workspace.js +176 -0
- package/dist/commands/workspace.js.map +1 -0
- package/dist/lib/api-client.d.ts +45 -0
- package/dist/lib/api-client.d.ts.map +1 -0
- package/dist/lib/api-client.js +198 -0
- package/dist/lib/api-client.js.map +1 -0
- package/dist/lib/auth-store.d.ts +47 -0
- package/dist/lib/auth-store.d.ts.map +1 -0
- package/dist/lib/auth-store.js +116 -0
- package/dist/lib/auth-store.js.map +1 -0
- package/dist/lib/brand.d.ts +32 -0
- package/dist/lib/brand.d.ts.map +1 -0
- package/dist/lib/brand.js +32 -0
- package/dist/lib/brand.js.map +1 -0
- package/dist/lib/build-schema.d.ts +97 -0
- package/dist/lib/build-schema.d.ts.map +1 -0
- package/dist/lib/build-schema.js +139 -0
- package/dist/lib/build-schema.js.map +1 -0
- package/dist/lib/canvas-read.d.ts +54 -0
- package/dist/lib/canvas-read.d.ts.map +1 -0
- package/dist/lib/canvas-read.js +145 -0
- package/dist/lib/canvas-read.js.map +1 -0
- package/dist/lib/claude-session.d.ts +73 -0
- package/dist/lib/claude-session.d.ts.map +1 -0
- package/dist/lib/claude-session.js +104 -0
- package/dist/lib/claude-session.js.map +1 -0
- package/dist/lib/config.d.ts +28 -0
- package/dist/lib/config.d.ts.map +1 -0
- package/dist/lib/config.js +66 -0
- package/dist/lib/config.js.map +1 -0
- package/dist/lib/env.d.ts +14 -0
- package/dist/lib/env.d.ts.map +1 -0
- package/dist/lib/env.js +16 -0
- package/dist/lib/env.js.map +1 -0
- package/dist/lib/errors.d.ts +47 -0
- package/dist/lib/errors.d.ts.map +1 -0
- package/dist/lib/errors.js +194 -0
- package/dist/lib/errors.js.map +1 -0
- package/dist/lib/fs-utils.d.ts +2 -0
- package/dist/lib/fs-utils.d.ts.map +1 -0
- package/dist/lib/fs-utils.js +16 -0
- package/dist/lib/fs-utils.js.map +1 -0
- package/dist/lib/install-hook.d.ts +86 -0
- package/dist/lib/install-hook.d.ts.map +1 -0
- package/dist/lib/install-hook.js +168 -0
- package/dist/lib/install-hook.js.map +1 -0
- package/dist/lib/install-mcp.d.ts +21 -0
- package/dist/lib/install-mcp.d.ts.map +1 -0
- package/dist/lib/install-mcp.js +133 -0
- package/dist/lib/install-mcp.js.map +1 -0
- package/dist/lib/install-skill.d.ts +49 -0
- package/dist/lib/install-skill.d.ts.map +1 -0
- package/dist/lib/install-skill.js +113 -0
- package/dist/lib/install-skill.js.map +1 -0
- package/dist/lib/output.d.ts +29 -0
- package/dist/lib/output.d.ts.map +1 -0
- package/dist/lib/output.js +78 -0
- package/dist/lib/output.js.map +1 -0
- package/dist/lib/resolve-note.d.ts +7 -0
- package/dist/lib/resolve-note.d.ts.map +1 -0
- package/dist/lib/resolve-note.js +23 -0
- package/dist/lib/resolve-note.js.map +1 -0
- package/dist/lib/stdin.d.ts +5 -0
- package/dist/lib/stdin.d.ts.map +1 -0
- package/dist/lib/stdin.js +11 -0
- package/dist/lib/stdin.js.map +1 -0
- package/dist/lib/style.d.ts +10 -0
- package/dist/lib/style.d.ts.map +1 -0
- package/dist/lib/style.js +17 -0
- package/dist/lib/style.js.map +1 -0
- package/dist/lib/themes.d.ts +44 -0
- package/dist/lib/themes.d.ts.map +1 -0
- package/dist/lib/themes.js +168 -0
- package/dist/lib/themes.js.map +1 -0
- package/dist/mcp-server.d.ts +3 -0
- package/dist/mcp-server.d.ts.map +1 -0
- package/dist/mcp-server.js +782 -0
- package/dist/mcp-server.js.map +1 -0
- package/package.json +66 -0
- package/skills/cnotes/SKILL.md +680 -0
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Install a Claude Code SessionStart hook that captures the session id.
|
|
3
|
+
*
|
|
4
|
+
* The hook runs `cnotes claude-hook` at session start, which writes the current
|
|
5
|
+
* session id to a file (see lib/claude-session.ts). `cnotes operations begin` then
|
|
6
|
+
* stamps it on the operation row, so an agent run on the timeline can be traced
|
|
7
|
+
* back to — and resumed from — the conversation that produced it.
|
|
8
|
+
*
|
|
9
|
+
* Target: the USER-GLOBAL ~/.claude/settings.json (so capture works in every
|
|
10
|
+
* repo, not just one). We preserve all existing settings + other hooks, back up
|
|
11
|
+
* the file before writing, and are idempotent (a second install is a no-op).
|
|
12
|
+
*
|
|
13
|
+
* Unlike the MCP installer, a parse failure here ABORTS rather than starting
|
|
14
|
+
* fresh: settings.json holds the user's entire Claude Code config, so silently
|
|
15
|
+
* overwriting it would be destructive.
|
|
16
|
+
*/
|
|
17
|
+
import { readFileSync, writeFileSync, renameSync, mkdirSync, existsSync, copyFileSync, } from "fs";
|
|
18
|
+
import { dirname, resolve } from "path";
|
|
19
|
+
import { homedir } from "os";
|
|
20
|
+
/** The user-global Claude Code settings file. */
|
|
21
|
+
export const CLAUDE_SETTINGS_PATH = resolve(homedir(), ".claude", "settings.json");
|
|
22
|
+
/** The command the hook runs when `cnotes` is on PATH (a global install). */
|
|
23
|
+
export const SESSION_HOOK_COMMAND = "cnotes claude-hook";
|
|
24
|
+
/**
|
|
25
|
+
* Fallback command for when `cnotes` won't be on PATH — e.g. the user ran
|
|
26
|
+
* `npx @creator-notes/cnotes init` without installing globally. Resolving the hook
|
|
27
|
+
* through npx keeps it working instead of failing `command not found` on every
|
|
28
|
+
* session start. Costs npx resolution latency per session, so we only use it
|
|
29
|
+
* when `cnotes` genuinely isn't available. `npx cnotes` does NOT resolve (the published
|
|
30
|
+
* package is @creator-notes/cnotes); detection tolerates this form for idempotency.
|
|
31
|
+
*/
|
|
32
|
+
export const SESSION_HOOK_COMMAND_NPX = "npx @creator-notes/cnotes claude-hook";
|
|
33
|
+
/**
|
|
34
|
+
* A plain JSON object — the only shape a Claude Code settings.json can validly
|
|
35
|
+
* take. Arrays, `null`, and primitives parse as valid JSON but aren't a config
|
|
36
|
+
* we can safely merge into, so callers treat them like a parse failure rather
|
|
37
|
+
* than silently clobbering the file. Exported for direct unit testing.
|
|
38
|
+
*/
|
|
39
|
+
export function isPlainSettingsObject(value) {
|
|
40
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Identify our entry by the `claude-hook` command (tolerates both the `cnotes` and
|
|
44
|
+
* `npx …` forms). Exported for direct unit testing.
|
|
45
|
+
*/
|
|
46
|
+
export function groupHasSessionHook(group) {
|
|
47
|
+
return (Array.isArray(group.hooks) &&
|
|
48
|
+
group.hooks.some((h) => typeof h.command === "string" && h.command.includes("claude-hook")));
|
|
49
|
+
}
|
|
50
|
+
/** True when any SessionStart group already carries our hook. Exported for tests. */
|
|
51
|
+
export function settingsHaveSessionHook(settings) {
|
|
52
|
+
const groups = settings.hooks?.SessionStart;
|
|
53
|
+
return Array.isArray(groups) && groups.some(groupHasSessionHook);
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Rewrite any SessionStart hook that targets our `claude-hook` subcommand but
|
|
57
|
+
* isn't the command we're installing — e.g. a pre-rename `cn claude-hook` entry
|
|
58
|
+
* left dead once the `cn` binary was dropped — to `command`, in place. Returns
|
|
59
|
+
* true if anything changed. Exported for direct unit testing.
|
|
60
|
+
*/
|
|
61
|
+
export function upgradeStaleSessionHooks(settings, command) {
|
|
62
|
+
// Both shipped forms are valid and must be left alone — only a foreign
|
|
63
|
+
// claude-hook command (e.g. the dropped `cn claude-hook`) is rewritten.
|
|
64
|
+
const current = new Set([SESSION_HOOK_COMMAND, SESSION_HOOK_COMMAND_NPX]);
|
|
65
|
+
const groups = settings.hooks?.SessionStart;
|
|
66
|
+
if (!Array.isArray(groups))
|
|
67
|
+
return false;
|
|
68
|
+
let changed = false;
|
|
69
|
+
for (const group of groups) {
|
|
70
|
+
if (!Array.isArray(group.hooks))
|
|
71
|
+
continue;
|
|
72
|
+
for (const h of group.hooks) {
|
|
73
|
+
if (typeof h.command === "string" &&
|
|
74
|
+
h.command.includes("claude-hook") &&
|
|
75
|
+
!current.has(h.command)) {
|
|
76
|
+
h.command = command;
|
|
77
|
+
changed = true;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return changed;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* True when a `cnotes claude-hook` SessionStart entry already exists. Read-only —
|
|
85
|
+
* lets callers skip prompting on re-runs without mutating anything. Returns
|
|
86
|
+
* false (rather than throwing) when the file is missing or unreadable.
|
|
87
|
+
*/
|
|
88
|
+
export function isClaudeSessionHookInstalled() {
|
|
89
|
+
try {
|
|
90
|
+
const settings = JSON.parse(readFileSync(CLAUDE_SETTINGS_PATH, "utf-8"));
|
|
91
|
+
return settingsHaveSessionHook(settings);
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Idempotently add the SessionStart hook to ~/.claude/settings.json.
|
|
99
|
+
* Preserves existing settings + hooks, backs up an existing file first.
|
|
100
|
+
*
|
|
101
|
+
* @throws if an existing settings.json is present but not valid JSON — we will
|
|
102
|
+
* not overwrite the user's whole config; the caller should surface the manual
|
|
103
|
+
* install instructions instead.
|
|
104
|
+
*/
|
|
105
|
+
export function installClaudeSessionHook(command = SESSION_HOOK_COMMAND) {
|
|
106
|
+
const configPath = CLAUDE_SETTINGS_PATH;
|
|
107
|
+
const existed = existsSync(configPath);
|
|
108
|
+
let settings = {};
|
|
109
|
+
if (existed) {
|
|
110
|
+
const raw = readFileSync(configPath, "utf-8");
|
|
111
|
+
let parsed;
|
|
112
|
+
try {
|
|
113
|
+
parsed = JSON.parse(raw);
|
|
114
|
+
}
|
|
115
|
+
catch {
|
|
116
|
+
throw new Error(`${configPath} is not valid JSON — leaving it untouched. Add the SessionStart hook manually.`);
|
|
117
|
+
}
|
|
118
|
+
// Valid JSON but not an object (e.g. an array or a primitive) is a config
|
|
119
|
+
// we don't understand — refuse rather than reformat it into something that
|
|
120
|
+
// would silently drop the hook (and falsely report success).
|
|
121
|
+
if (!isPlainSettingsObject(parsed)) {
|
|
122
|
+
throw new Error(`${configPath} is not a JSON object — leaving it untouched. Add the SessionStart hook manually.`);
|
|
123
|
+
}
|
|
124
|
+
settings = parsed;
|
|
125
|
+
}
|
|
126
|
+
// Upgrade a pre-rename hook (e.g. a now-dead `cn claude-hook`) to the current
|
|
127
|
+
// command in place, so an existing install heals instead of keeping a broken
|
|
128
|
+
// SessionStart entry. A genuinely up-to-date hook needs no write.
|
|
129
|
+
const upgraded = upgradeStaleSessionHooks(settings, command);
|
|
130
|
+
if (existed && settingsHaveSessionHook(settings) && !upgraded) {
|
|
131
|
+
return { configPath, status: "already-present" };
|
|
132
|
+
}
|
|
133
|
+
// Back up an existing (valid) file before mutating it.
|
|
134
|
+
let backedUp;
|
|
135
|
+
if (existed) {
|
|
136
|
+
backedUp = configPath + ".bak";
|
|
137
|
+
try {
|
|
138
|
+
copyFileSync(configPath, backedUp);
|
|
139
|
+
}
|
|
140
|
+
catch {
|
|
141
|
+
backedUp = undefined;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
// Append our hook only when none exists yet. If we just upgraded a stale
|
|
145
|
+
// entry, it's already present — we only need to persist the rewrite.
|
|
146
|
+
if (!settingsHaveSessionHook(settings)) {
|
|
147
|
+
if (!settings.hooks || typeof settings.hooks !== "object") {
|
|
148
|
+
settings.hooks = {};
|
|
149
|
+
}
|
|
150
|
+
const hooks = settings.hooks;
|
|
151
|
+
if (!Array.isArray(hooks.SessionStart)) {
|
|
152
|
+
hooks.SessionStart = [];
|
|
153
|
+
}
|
|
154
|
+
// Append a fresh group rather than mutating an existing one, so any
|
|
155
|
+
// user-defined SessionStart groups (with their own matchers) are untouched.
|
|
156
|
+
// No matcher → fires on every source (startup, resume, clear, compact); the
|
|
157
|
+
// session id is stable across those, so re-stamping just refreshes the file.
|
|
158
|
+
hooks.SessionStart.push({ hooks: [{ type: "command", command }] });
|
|
159
|
+
}
|
|
160
|
+
mkdirSync(dirname(configPath), { recursive: true });
|
|
161
|
+
// Write atomically (temp file + rename) so an interrupted write can never
|
|
162
|
+
// leave the user's whole Claude Code config truncated.
|
|
163
|
+
const tmpPath = configPath + ".tmp";
|
|
164
|
+
writeFileSync(tmpPath, JSON.stringify(settings, null, 2) + "\n", "utf-8");
|
|
165
|
+
renameSync(tmpPath, configPath);
|
|
166
|
+
return { configPath, status: existed ? "installed" : "created", backedUp };
|
|
167
|
+
}
|
|
168
|
+
//# sourceMappingURL=install-hook.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"install-hook.js","sourceRoot":"","sources":["../../src/lib/install-hook.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auto-install MCP server config into Claude Desktop or Codex config files.
|
|
3
|
+
*
|
|
4
|
+
* Reads existing config, merges the CN MCP server entry, writes back.
|
|
5
|
+
* Creates config file/directory if they don't exist.
|
|
6
|
+
*/
|
|
7
|
+
export interface McpInstallConfig {
|
|
8
|
+
serverUrl: string;
|
|
9
|
+
token: string;
|
|
10
|
+
workspaceId?: string;
|
|
11
|
+
}
|
|
12
|
+
export type McpTarget = "claude" | "codex";
|
|
13
|
+
interface InstallResult {
|
|
14
|
+
configPath: string;
|
|
15
|
+
created: boolean;
|
|
16
|
+
backedUp?: string;
|
|
17
|
+
}
|
|
18
|
+
export declare function getConfigPath(target: McpTarget): string;
|
|
19
|
+
export declare function installMcpConfig(target: McpTarget, cfg: McpInstallConfig): InstallResult;
|
|
20
|
+
export {};
|
|
21
|
+
//# sourceMappingURL=install-mcp.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"install-mcp.d.ts","sourceRoot":"","sources":["../../src/lib/install-mcp.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAOH,MAAM,WAAW,gBAAgB;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,MAAM,SAAS,GAAG,QAAQ,GAAG,OAAO,CAAC;AAE3C,UAAU,aAAa;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAuBD,wBAAgB,aAAa,CAAC,MAAM,EAAE,SAAS,GAAG,MAAM,CAEvD;AAwHD,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,EAAE,gBAAgB,GAAG,aAAa,CAExF"}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auto-install MCP server config into Claude Desktop or Codex config files.
|
|
3
|
+
*
|
|
4
|
+
* Reads existing config, merges the CN MCP server entry, writes back.
|
|
5
|
+
* Creates config file/directory if they don't exist.
|
|
6
|
+
*/
|
|
7
|
+
import { readFileSync, writeFileSync, mkdirSync, existsSync, copyFileSync } from "fs";
|
|
8
|
+
import { dirname, resolve } from "path";
|
|
9
|
+
import { homedir } from "os";
|
|
10
|
+
import { fileURLToPath } from "url";
|
|
11
|
+
// ── Path resolution ─────────────────────────────────────────
|
|
12
|
+
function expandHome(p) {
|
|
13
|
+
return p.startsWith("~") ? resolve(homedir(), p.slice(2)) : p;
|
|
14
|
+
}
|
|
15
|
+
function getClaudeConfigPath() {
|
|
16
|
+
switch (process.platform) {
|
|
17
|
+
case "win32":
|
|
18
|
+
return resolve(process.env.APPDATA || resolve(homedir(), "AppData", "Roaming"), "Claude", "claude_desktop_config.json");
|
|
19
|
+
case "darwin":
|
|
20
|
+
return resolve(homedir(), "Library", "Application Support", "Claude", "claude_desktop_config.json");
|
|
21
|
+
default:
|
|
22
|
+
return resolve(homedir(), ".config", "Claude", "claude_desktop_config.json");
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function getCodexConfigPath() {
|
|
26
|
+
return expandHome("~/.codex/config.toml");
|
|
27
|
+
}
|
|
28
|
+
export function getConfigPath(target) {
|
|
29
|
+
return target === "claude" ? getClaudeConfigPath() : getCodexConfigPath();
|
|
30
|
+
}
|
|
31
|
+
// ── MCP server entry ────────────────────────────────────────
|
|
32
|
+
function getMcpServerPath() {
|
|
33
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
34
|
+
return resolve(__dirname, "..", "mcp-server.js");
|
|
35
|
+
}
|
|
36
|
+
function buildMcpEntry(cfg) {
|
|
37
|
+
return {
|
|
38
|
+
command: "node",
|
|
39
|
+
args: [getMcpServerPath()],
|
|
40
|
+
env: {
|
|
41
|
+
CN_SERVER: cfg.serverUrl,
|
|
42
|
+
CN_TOKEN: cfg.token,
|
|
43
|
+
CN_WORKSPACE: cfg.workspaceId || "",
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
// ── Claude Desktop (JSON) ───────────────────────────────────
|
|
48
|
+
function installClaude(cfg) {
|
|
49
|
+
const configPath = getClaudeConfigPath();
|
|
50
|
+
const dir = dirname(configPath);
|
|
51
|
+
mkdirSync(dir, { recursive: true });
|
|
52
|
+
let existing = {};
|
|
53
|
+
let created = true;
|
|
54
|
+
let backedUp;
|
|
55
|
+
if (existsSync(configPath)) {
|
|
56
|
+
created = false;
|
|
57
|
+
const raw = readFileSync(configPath, "utf-8");
|
|
58
|
+
try {
|
|
59
|
+
existing = JSON.parse(raw);
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
// Corrupt file — back it up and start fresh
|
|
63
|
+
backedUp = configPath + ".bak";
|
|
64
|
+
copyFileSync(configPath, backedUp);
|
|
65
|
+
existing = {};
|
|
66
|
+
}
|
|
67
|
+
// Back up valid files too (overwrite previous .bak)
|
|
68
|
+
if (!backedUp) {
|
|
69
|
+
backedUp = configPath + ".bak";
|
|
70
|
+
copyFileSync(configPath, backedUp);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
// Merge — preserve other MCP servers
|
|
74
|
+
const mcpServers = existing.mcpServers || {};
|
|
75
|
+
delete mcpServers.cn; // drop the pre-rename server key if present
|
|
76
|
+
mcpServers.cnotes = buildMcpEntry(cfg);
|
|
77
|
+
existing.mcpServers = mcpServers;
|
|
78
|
+
writeFileSync(configPath, JSON.stringify(existing, null, 2) + "\n", "utf-8");
|
|
79
|
+
return { configPath, created, backedUp: created ? undefined : backedUp };
|
|
80
|
+
}
|
|
81
|
+
// ── Codex (TOML) ────────────────────────────────────────────
|
|
82
|
+
/**
|
|
83
|
+
* Simple TOML merge: replaces or appends the [mcp_servers.cnotes] block.
|
|
84
|
+
* Avoids a TOML dependency by doing targeted string manipulation.
|
|
85
|
+
*/
|
|
86
|
+
function installCodex(cfg) {
|
|
87
|
+
const configPath = getCodexConfigPath();
|
|
88
|
+
const dir = dirname(configPath);
|
|
89
|
+
mkdirSync(dir, { recursive: true });
|
|
90
|
+
const entry = buildMcpEntry(cfg);
|
|
91
|
+
const block = [
|
|
92
|
+
`[mcp_servers.cnotes]`,
|
|
93
|
+
`command = "${entry.command}"`,
|
|
94
|
+
`args = [${entry.args.map(a => `"${a}"`).join(", ")}]`,
|
|
95
|
+
``,
|
|
96
|
+
`[mcp_servers.cnotes.env]`,
|
|
97
|
+
`CN_SERVER = "${entry.env.CN_SERVER}"`,
|
|
98
|
+
`CN_TOKEN = "${entry.env.CN_TOKEN}"`,
|
|
99
|
+
`CN_WORKSPACE = "${entry.env.CN_WORKSPACE}"`,
|
|
100
|
+
].join("\n");
|
|
101
|
+
let created = true;
|
|
102
|
+
let backedUp;
|
|
103
|
+
if (existsSync(configPath)) {
|
|
104
|
+
created = false;
|
|
105
|
+
backedUp = configPath + ".bak";
|
|
106
|
+
copyFileSync(configPath, backedUp);
|
|
107
|
+
let content = readFileSync(configPath, "utf-8");
|
|
108
|
+
// Remove existing [mcp_servers.cnotes] and [mcp_servers.cnotes.env] blocks
|
|
109
|
+
// Match from [mcp_servers.cnotes] up to the next [section] or end of file
|
|
110
|
+
const cnotesBlockPattern = /\[mcp_servers\.cnotes\][\s\S]*?(?=\n\[(?!mcp_servers\.cnotes[\].])|$)/g;
|
|
111
|
+
content = content.replace(cnotesBlockPattern, "");
|
|
112
|
+
// Also drop the pre-rename [mcp_servers.cn] / [mcp_servers.cn.env] blocks
|
|
113
|
+
// so a Codex user migrating from `cn` doesn't keep a duplicate server
|
|
114
|
+
// pointing at the same mcp-server.js (parity with the Claude path's
|
|
115
|
+
// `delete mcpServers.cn`).
|
|
116
|
+
const legacyBlockPattern = /\[mcp_servers\.cn\][\s\S]*?(?=\n\[(?!mcp_servers\.cn[\].])|$)/g;
|
|
117
|
+
content = content.replace(legacyBlockPattern, "");
|
|
118
|
+
// Clean up extra blank lines left behind
|
|
119
|
+
content = content.replace(/\n{3,}/g, "\n\n").trimEnd();
|
|
120
|
+
// Append the new block
|
|
121
|
+
content = content + "\n\n" + block + "\n";
|
|
122
|
+
writeFileSync(configPath, content, "utf-8");
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
writeFileSync(configPath, block + "\n", "utf-8");
|
|
126
|
+
}
|
|
127
|
+
return { configPath, created, backedUp: created ? undefined : backedUp };
|
|
128
|
+
}
|
|
129
|
+
// ── Public API ──────────────────────────────────────────────
|
|
130
|
+
export function installMcpConfig(target, cfg) {
|
|
131
|
+
return target === "claude" ? installClaude(cfg) : installCodex(cfg);
|
|
132
|
+
}
|
|
133
|
+
//# sourceMappingURL=install-mcp.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"install-mcp.js","sourceRoot":"","sources":["../../src/lib/install-mcp.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auto-install the `cnotes` skill so coding agents auto-discover it.
|
|
3
|
+
*
|
|
4
|
+
* The same bundled skill feeds two targets:
|
|
5
|
+
* - Claude Code → ~/.claude/skills/cnotes/SKILL.md
|
|
6
|
+
* - OpenAI Codex → ~/.codex/skills/cnotes/SKILL.md
|
|
7
|
+
*
|
|
8
|
+
* The skill ships inside the published package at <package>/skills/cnotes/SKILL.md.
|
|
9
|
+
* At runtime this file lives at <package>/dist/lib/install-skill.js, so the
|
|
10
|
+
* skill source is two directories up.
|
|
11
|
+
*/
|
|
12
|
+
/** The agents we ship a discoverable skill for. */
|
|
13
|
+
export type SkillTarget = "claude" | "codex";
|
|
14
|
+
export declare const SKILL_TARGETS: SkillTarget[];
|
|
15
|
+
/** Human-facing label for a target (used in init/output messages). */
|
|
16
|
+
export declare function skillTargetLabel(target: SkillTarget): string;
|
|
17
|
+
export interface SkillInstallResult {
|
|
18
|
+
target: SkillTarget;
|
|
19
|
+
installed: boolean;
|
|
20
|
+
destPath: string;
|
|
21
|
+
alreadyUpToDate?: boolean;
|
|
22
|
+
}
|
|
23
|
+
/** Where the skill lives on disk inside the installed package. */
|
|
24
|
+
export declare function getBundledSkillPath(): string;
|
|
25
|
+
/** Where the skill should be copied to for a given agent. */
|
|
26
|
+
export declare function getSkillDestPath(target?: SkillTarget): string;
|
|
27
|
+
/** True if the user appears to use this agent (its config dir exists). */
|
|
28
|
+
export declare function hasAgentConfigDir(target: SkillTarget): boolean;
|
|
29
|
+
/** True if the destination skill already exists for a target. */
|
|
30
|
+
export declare function skillAlreadyInstalled(target?: SkillTarget): boolean;
|
|
31
|
+
/** True if a target still has the old `cn` skill installed. */
|
|
32
|
+
export declare function legacySkillInstalled(target: SkillTarget): boolean;
|
|
33
|
+
/**
|
|
34
|
+
* Refresh ALREADY-installed skill copies if the bundled copy has changed.
|
|
35
|
+
*
|
|
36
|
+
* Runs on every CLI invocation (cheap: one file compare per target). It never
|
|
37
|
+
* installs for an agent the user hasn't opted into — it only updates a skill
|
|
38
|
+
* that already exists, so a plain `npm update -g @creator-notes/cnotes` is enough
|
|
39
|
+
* to get the latest skill for both Claude and Codex without re-running
|
|
40
|
+
* `cnotes init`. Best-effort: per-target errors are swallowed so skill maintenance
|
|
41
|
+
* can never break a command.
|
|
42
|
+
*/
|
|
43
|
+
export declare function refreshInstalledSkills(): SkillInstallResult[];
|
|
44
|
+
/**
|
|
45
|
+
* Copy the bundled skill into a target agent's skills dir.
|
|
46
|
+
* Caller is responsible for any prompting / confirmation.
|
|
47
|
+
*/
|
|
48
|
+
export declare function installSkill(target?: SkillTarget): SkillInstallResult;
|
|
49
|
+
//# sourceMappingURL=install-skill.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"install-skill.d.ts","sourceRoot":"","sources":["../../src/lib/install-skill.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAOH,mDAAmD;AACnD,MAAM,MAAM,WAAW,GAAG,QAAQ,GAAG,OAAO,CAAC;AAE7C,eAAO,MAAM,aAAa,EAAE,WAAW,EAAwB,CAAC;AAEhE,sEAAsE;AACtE,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,CAE5D;AAED,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,WAAW,CAAC;IACpB,SAAS,EAAE,OAAO,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAOD,kEAAkE;AAClE,wBAAgB,mBAAmB,IAAI,MAAM,CAG5C;AAED,6DAA6D;AAC7D,wBAAgB,gBAAgB,CAAC,MAAM,GAAE,WAAsB,GAAG,MAAM,CAEvE;AAED,0EAA0E;AAC1E,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAE9D;AAED,iEAAiE;AACjE,wBAAgB,qBAAqB,CAAC,MAAM,GAAE,WAAsB,GAAG,OAAO,CAE7E;AAOD,+DAA+D;AAC/D,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAEjE;AAoBD;;;;;;;;;GASG;AACH,wBAAgB,sBAAsB,IAAI,kBAAkB,EAAE,CAc7D;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,MAAM,GAAE,WAAsB,GAAG,kBAAkB,CAgB/E"}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auto-install the `cnotes` skill so coding agents auto-discover it.
|
|
3
|
+
*
|
|
4
|
+
* The same bundled skill feeds two targets:
|
|
5
|
+
* - Claude Code → ~/.claude/skills/cnotes/SKILL.md
|
|
6
|
+
* - OpenAI Codex → ~/.codex/skills/cnotes/SKILL.md
|
|
7
|
+
*
|
|
8
|
+
* The skill ships inside the published package at <package>/skills/cnotes/SKILL.md.
|
|
9
|
+
* At runtime this file lives at <package>/dist/lib/install-skill.js, so the
|
|
10
|
+
* skill source is two directories up.
|
|
11
|
+
*/
|
|
12
|
+
import { copyFileSync, existsSync, mkdirSync, readFileSync, rmSync } from "fs";
|
|
13
|
+
import { dirname, resolve } from "path";
|
|
14
|
+
import { homedir } from "os";
|
|
15
|
+
import { fileURLToPath } from "url";
|
|
16
|
+
export const SKILL_TARGETS = ["claude", "codex"];
|
|
17
|
+
/** Human-facing label for a target (used in init/output messages). */
|
|
18
|
+
export function skillTargetLabel(target) {
|
|
19
|
+
return target === "claude" ? "Claude Code" : "Codex";
|
|
20
|
+
}
|
|
21
|
+
/** The config dir each agent reads its skills from. */
|
|
22
|
+
function agentConfigDir(target) {
|
|
23
|
+
return resolve(homedir(), target === "claude" ? ".claude" : ".codex");
|
|
24
|
+
}
|
|
25
|
+
/** Where the skill lives on disk inside the installed package. */
|
|
26
|
+
export function getBundledSkillPath() {
|
|
27
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
28
|
+
return resolve(__dirname, "..", "..", "skills", "cnotes", "SKILL.md");
|
|
29
|
+
}
|
|
30
|
+
/** Where the skill should be copied to for a given agent. */
|
|
31
|
+
export function getSkillDestPath(target = "claude") {
|
|
32
|
+
return resolve(agentConfigDir(target), "skills", "cnotes", "SKILL.md");
|
|
33
|
+
}
|
|
34
|
+
/** True if the user appears to use this agent (its config dir exists). */
|
|
35
|
+
export function hasAgentConfigDir(target) {
|
|
36
|
+
return existsSync(agentConfigDir(target));
|
|
37
|
+
}
|
|
38
|
+
/** True if the destination skill already exists for a target. */
|
|
39
|
+
export function skillAlreadyInstalled(target = "claude") {
|
|
40
|
+
return existsSync(getSkillDestPath(target));
|
|
41
|
+
}
|
|
42
|
+
/** The pre-rename skill location (`skills/cn/`), kept only to migrate away from it. */
|
|
43
|
+
function getLegacySkillDir(target) {
|
|
44
|
+
return resolve(agentConfigDir(target), "skills", "cn");
|
|
45
|
+
}
|
|
46
|
+
/** True if a target still has the old `cn` skill installed. */
|
|
47
|
+
export function legacySkillInstalled(target) {
|
|
48
|
+
return existsSync(resolve(getLegacySkillDir(target), "SKILL.md"));
|
|
49
|
+
}
|
|
50
|
+
/** Delete the pre-rename `cn` skill dir if present (best-effort, idempotent). */
|
|
51
|
+
function removeLegacySkill(target) {
|
|
52
|
+
try {
|
|
53
|
+
const dir = getLegacySkillDir(target);
|
|
54
|
+
if (existsSync(dir))
|
|
55
|
+
rmSync(dir, { recursive: true, force: true });
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
/* ignore — a stale legacy skill is harmless; never fail a command over it */
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
function filesAreIdentical(a, b) {
|
|
62
|
+
try {
|
|
63
|
+
return readFileSync(a).equals(readFileSync(b));
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Refresh ALREADY-installed skill copies if the bundled copy has changed.
|
|
71
|
+
*
|
|
72
|
+
* Runs on every CLI invocation (cheap: one file compare per target). It never
|
|
73
|
+
* installs for an agent the user hasn't opted into — it only updates a skill
|
|
74
|
+
* that already exists, so a plain `npm update -g @creator-notes/cnotes` is enough
|
|
75
|
+
* to get the latest skill for both Claude and Codex without re-running
|
|
76
|
+
* `cnotes init`. Best-effort: per-target errors are swallowed so skill maintenance
|
|
77
|
+
* can never break a command.
|
|
78
|
+
*/
|
|
79
|
+
export function refreshInstalledSkills() {
|
|
80
|
+
const results = [];
|
|
81
|
+
for (const target of SKILL_TARGETS) {
|
|
82
|
+
try {
|
|
83
|
+
// Refresh if the current skill is installed, OR migrate a target that
|
|
84
|
+
// still only has the pre-rename `cn` skill (installSkill removes it).
|
|
85
|
+
if (skillAlreadyInstalled(target) || legacySkillInstalled(target)) {
|
|
86
|
+
results.push(installSkill(target));
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
/* ignore this target — keep refreshing the others */
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return results;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Copy the bundled skill into a target agent's skills dir.
|
|
97
|
+
* Caller is responsible for any prompting / confirmation.
|
|
98
|
+
*/
|
|
99
|
+
export function installSkill(target = "claude") {
|
|
100
|
+
const source = getBundledSkillPath();
|
|
101
|
+
const dest = getSkillDestPath(target);
|
|
102
|
+
if (!existsSync(source)) {
|
|
103
|
+
throw new Error(`Bundled skill not found at ${source}`);
|
|
104
|
+
}
|
|
105
|
+
if (existsSync(dest) && filesAreIdentical(source, dest)) {
|
|
106
|
+
return { target, installed: false, destPath: dest, alreadyUpToDate: true };
|
|
107
|
+
}
|
|
108
|
+
mkdirSync(dirname(dest), { recursive: true });
|
|
109
|
+
copyFileSync(source, dest);
|
|
110
|
+
removeLegacySkill(target); // drop the old `cn` skill once the new one is in place
|
|
111
|
+
return { target, installed: true, destPath: dest };
|
|
112
|
+
}
|
|
113
|
+
//# sourceMappingURL=install-skill.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"install-skill.js","sourceRoot":"","sources":["../../src/lib/install-skill.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Output formatting utilities.
|
|
3
|
+
*
|
|
4
|
+
* JSON mode: raw API response.
|
|
5
|
+
* Human mode: formatted tables/text.
|
|
6
|
+
* Quiet mode: minimal output (IDs only).
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Project an object (or array of objects) down to a set of top-level fields.
|
|
10
|
+
* No-op when `fields` is empty/undefined. Lets agents trim list/get output to
|
|
11
|
+
* just the keys they need (e.g. `--fields displayId,title`) instead of paying
|
|
12
|
+
* for the full record in their context window.
|
|
13
|
+
*/
|
|
14
|
+
export declare function projectFields(data: unknown, fields?: string[]): unknown;
|
|
15
|
+
export declare function outputJson(data: unknown, fields?: string[]): void;
|
|
16
|
+
export declare function outputQuiet(value: string): void;
|
|
17
|
+
/**
|
|
18
|
+
* Print a simple table with aligned columns.
|
|
19
|
+
*/
|
|
20
|
+
export declare function outputTable(headers: string[], rows: string[][]): void;
|
|
21
|
+
/**
|
|
22
|
+
* Format a timestamp as relative time (e.g., "2h ago", "3d ago").
|
|
23
|
+
*/
|
|
24
|
+
export declare function timeAgo(timestamp: number | string): string;
|
|
25
|
+
/**
|
|
26
|
+
* Truncate a string to a max length with ellipsis.
|
|
27
|
+
*/
|
|
28
|
+
export declare function truncate(str: string, max: number): string;
|
|
29
|
+
//# sourceMappingURL=output.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"output.d.ts","sourceRoot":"","sources":["../../src/lib/output.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,MAAM,EAAE,GAAG,OAAO,CAYvE;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,CAEjE;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAE/C;AAED;;GAEG;AACH,wBAAgB,WAAW,CACzB,OAAO,EAAE,MAAM,EAAE,EACjB,IAAI,EAAE,MAAM,EAAE,EAAE,GACf,IAAI,CAeN;AAED;;GAEG;AACH,wBAAgB,OAAO,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAa1D;AAED;;GAEG;AACH,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAGzD"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Output formatting utilities.
|
|
3
|
+
*
|
|
4
|
+
* JSON mode: raw API response.
|
|
5
|
+
* Human mode: formatted tables/text.
|
|
6
|
+
* Quiet mode: minimal output (IDs only).
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Project an object (or array of objects) down to a set of top-level fields.
|
|
10
|
+
* No-op when `fields` is empty/undefined. Lets agents trim list/get output to
|
|
11
|
+
* just the keys they need (e.g. `--fields displayId,title`) instead of paying
|
|
12
|
+
* for the full record in their context window.
|
|
13
|
+
*/
|
|
14
|
+
export function projectFields(data, fields) {
|
|
15
|
+
if (!fields || fields.length === 0)
|
|
16
|
+
return data;
|
|
17
|
+
const pick = (obj) => {
|
|
18
|
+
if (!obj || typeof obj !== "object" || Array.isArray(obj))
|
|
19
|
+
return obj;
|
|
20
|
+
const rec = obj;
|
|
21
|
+
const out = {};
|
|
22
|
+
for (const f of fields) {
|
|
23
|
+
if (f in rec)
|
|
24
|
+
out[f] = rec[f];
|
|
25
|
+
}
|
|
26
|
+
return out;
|
|
27
|
+
};
|
|
28
|
+
return Array.isArray(data) ? data.map(pick) : pick(data);
|
|
29
|
+
}
|
|
30
|
+
export function outputJson(data, fields) {
|
|
31
|
+
console.log(JSON.stringify(projectFields(data, fields), null, 2));
|
|
32
|
+
}
|
|
33
|
+
export function outputQuiet(value) {
|
|
34
|
+
console.log(value);
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Print a simple table with aligned columns.
|
|
38
|
+
*/
|
|
39
|
+
export function outputTable(headers, rows) {
|
|
40
|
+
const widths = headers.map((h, i) => Math.max(h.length, ...rows.map((r) => (r[i] || "").length)));
|
|
41
|
+
const headerLine = headers
|
|
42
|
+
.map((h, i) => h.padEnd(widths[i]))
|
|
43
|
+
.join(" ");
|
|
44
|
+
const separator = widths.map((w) => "-".repeat(w)).join(" ");
|
|
45
|
+
console.log(headerLine);
|
|
46
|
+
console.log(separator);
|
|
47
|
+
for (const row of rows) {
|
|
48
|
+
console.log(row.map((c, i) => (c || "").padEnd(widths[i])).join(" "));
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Format a timestamp as relative time (e.g., "2h ago", "3d ago").
|
|
53
|
+
*/
|
|
54
|
+
export function timeAgo(timestamp) {
|
|
55
|
+
const ms = typeof timestamp === "string" ? new Date(timestamp).getTime() : timestamp;
|
|
56
|
+
const now = Date.now();
|
|
57
|
+
const diff = now - ms;
|
|
58
|
+
const seconds = Math.floor(diff / 1000);
|
|
59
|
+
const minutes = Math.floor(seconds / 60);
|
|
60
|
+
const hours = Math.floor(minutes / 60);
|
|
61
|
+
const days = Math.floor(hours / 24);
|
|
62
|
+
if (days > 0)
|
|
63
|
+
return `${days}d ago`;
|
|
64
|
+
if (hours > 0)
|
|
65
|
+
return `${hours}h ago`;
|
|
66
|
+
if (minutes > 0)
|
|
67
|
+
return `${minutes}m ago`;
|
|
68
|
+
return "just now";
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Truncate a string to a max length with ellipsis.
|
|
72
|
+
*/
|
|
73
|
+
export function truncate(str, max) {
|
|
74
|
+
if (str.length <= max)
|
|
75
|
+
return str;
|
|
76
|
+
return str.slice(0, max - 1) + "\u2026";
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=output.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"output.js","sourceRoot":"","sources":["../../src/lib/output.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { ApiClient } from "./api-client.js";
|
|
2
|
+
/**
|
|
3
|
+
* Resolve a display ID (e.g., "MEETING-12") to a Convex note ID.
|
|
4
|
+
* Falls back to treating the input as a Convex ID directly.
|
|
5
|
+
*/
|
|
6
|
+
export declare function resolveNoteId(api: ApiClient, workspaceId: string, idOrDisplayId: string): Promise<string>;
|
|
7
|
+
//# sourceMappingURL=resolve-note.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolve-note.d.ts","sourceRoot":"","sources":["../../src/lib/resolve-note.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAG5C;;;GAGG;AACH,wBAAsB,aAAa,CACjC,GAAG,EAAE,SAAS,EACd,WAAW,EAAE,MAAM,EACnB,aAAa,EAAE,MAAM,GACpB,OAAO,CAAC,MAAM,CAAC,CAsBjB"}
|