pnote 0.3.0 → 0.4.1
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/dist/index.js +339 -54
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
|
-
import { Command as
|
|
4
|
+
import { Command as Command10 } from "commander";
|
|
5
5
|
|
|
6
6
|
// src/lib/errors.ts
|
|
7
7
|
import pc from "picocolors";
|
|
@@ -311,6 +311,16 @@ async function search(params, options = {}) {
|
|
|
311
311
|
if (params.limit) query.set("limit", String(params.limit));
|
|
312
312
|
return callRestApi("GET", `/search?${query.toString()}`, void 0, options);
|
|
313
313
|
}
|
|
314
|
+
async function checkPinStatus(options = {}) {
|
|
315
|
+
return callRestApi("GET", "/pin/status", void 0, options);
|
|
316
|
+
}
|
|
317
|
+
async function listProtectedNotes(params = {}, options = {}) {
|
|
318
|
+
const query = new URLSearchParams();
|
|
319
|
+
if (params.limit) query.set("limit", String(params.limit));
|
|
320
|
+
if (params.offset) query.set("offset", String(params.offset));
|
|
321
|
+
const queryString = query.toString();
|
|
322
|
+
return callRestApi("GET", `/pin/notes${queryString ? `?${queryString}` : ""}`, void 0, options);
|
|
323
|
+
}
|
|
314
324
|
async function listSharedTags(options = {}) {
|
|
315
325
|
return callRestApi("GET", "/share/tags", void 0, options);
|
|
316
326
|
}
|
|
@@ -832,6 +842,7 @@ async function listNotesAction(options, ctx) {
|
|
|
832
842
|
archived: options.archived ?? false,
|
|
833
843
|
pinned: options.pinned,
|
|
834
844
|
deleted: options.deleted ?? false,
|
|
845
|
+
protected: options.protected,
|
|
835
846
|
search: options.search,
|
|
836
847
|
limit: options.limit ? parseInt(options.limit, 10) : 50
|
|
837
848
|
},
|
|
@@ -946,10 +957,10 @@ async function confirm(message) {
|
|
|
946
957
|
input: process.stdin,
|
|
947
958
|
output: process.stdout
|
|
948
959
|
});
|
|
949
|
-
return new Promise((
|
|
960
|
+
return new Promise((resolve3) => {
|
|
950
961
|
rl.question(message + " [y/N] ", (answer) => {
|
|
951
962
|
rl.close();
|
|
952
|
-
|
|
963
|
+
resolve3(answer.toLowerCase() === "y" || answer.toLowerCase() === "yes");
|
|
953
964
|
});
|
|
954
965
|
});
|
|
955
966
|
}
|
|
@@ -1273,7 +1284,7 @@ async function promptForPin(noteTitle, hint, maxAttempts = PIN_MAX_ATTEMPTS) {
|
|
|
1273
1284
|
return null;
|
|
1274
1285
|
}
|
|
1275
1286
|
async function readPinHidden(prompt) {
|
|
1276
|
-
return new Promise((
|
|
1287
|
+
return new Promise((resolve3) => {
|
|
1277
1288
|
const rl = createInterface2({
|
|
1278
1289
|
input: process.stdin,
|
|
1279
1290
|
output: process.stderr,
|
|
@@ -1289,7 +1300,7 @@ async function readPinHidden(prompt) {
|
|
|
1289
1300
|
if (char === "\r" || char === "\n") {
|
|
1290
1301
|
process.stderr.write("\n");
|
|
1291
1302
|
cleanup();
|
|
1292
|
-
|
|
1303
|
+
resolve3(pin);
|
|
1293
1304
|
} else if (char === "") {
|
|
1294
1305
|
process.stderr.write("\n");
|
|
1295
1306
|
cleanup();
|
|
@@ -1424,7 +1435,7 @@ async function buildContext2(globalOpts) {
|
|
|
1424
1435
|
pin: pin ?? void 0
|
|
1425
1436
|
};
|
|
1426
1437
|
}
|
|
1427
|
-
var notesCommand = new Command3("notes").description("List and manage notes").option("--tag <tag>", 'Filter by tag (e.g., "AI/art")').option("--archived", "Show archived notes").option("--pinned", "Show only pinned notes").option("--deleted", "Show deleted notes").option("--search <query>", "Search notes by title").option("--limit <n>", "Limit number of results", "50").action(async (options, cmd) => {
|
|
1438
|
+
var notesCommand = new Command3("notes").description("List and manage notes").option("--tag <tag>", 'Filter by tag (e.g., "AI/art")').option("--archived", "Show archived notes").option("--pinned", "Show only pinned notes").option("--deleted", "Show deleted notes").option("--protected", "Show only PIN-protected notes").option("--search <query>", "Search notes by title").option("--limit <n>", "Limit number of results", "50").action(async (options, cmd) => {
|
|
1428
1439
|
const globalOpts = cmd.parent?.opts() || {};
|
|
1429
1440
|
const ctx = await buildContext2(globalOpts);
|
|
1430
1441
|
await listNotesAction(options, ctx);
|
|
@@ -1820,63 +1831,172 @@ import * as fs from "fs";
|
|
|
1820
1831
|
import * as path from "path";
|
|
1821
1832
|
import * as os from "os";
|
|
1822
1833
|
import pc11 from "picocolors";
|
|
1834
|
+
var AGENTS_STORE = ".agents/skills";
|
|
1835
|
+
var AGENT_SYMLINK_DIRS = [
|
|
1836
|
+
".claude/skills",
|
|
1837
|
+
".windsurf/skills",
|
|
1838
|
+
".augment/skills",
|
|
1839
|
+
".agent/skills"
|
|
1840
|
+
];
|
|
1841
|
+
function makeColors(useColor) {
|
|
1842
|
+
if (useColor) {
|
|
1843
|
+
return {
|
|
1844
|
+
green: pc11.green,
|
|
1845
|
+
dim: pc11.dim,
|
|
1846
|
+
yellow: pc11.yellow,
|
|
1847
|
+
bold: pc11.bold,
|
|
1848
|
+
red: pc11.red
|
|
1849
|
+
};
|
|
1850
|
+
}
|
|
1851
|
+
return {
|
|
1852
|
+
green: (s) => s,
|
|
1853
|
+
dim: (s) => s,
|
|
1854
|
+
yellow: (s) => s,
|
|
1855
|
+
bold: (s) => s,
|
|
1856
|
+
red: (s) => s
|
|
1857
|
+
};
|
|
1858
|
+
}
|
|
1859
|
+
function resolveMode(options) {
|
|
1860
|
+
if (options.dir) return { mode: "custom", root: path.resolve(options.dir) };
|
|
1861
|
+
if (options.project) return { mode: "project", root: process.cwd() };
|
|
1862
|
+
return { mode: "global", root: os.homedir() };
|
|
1863
|
+
}
|
|
1864
|
+
function ensureSymlink(linkPath, skillName, dryRun, c, log) {
|
|
1865
|
+
const relTarget = path.join("..", "..", AGENTS_STORE, skillName);
|
|
1866
|
+
if (dryRun) {
|
|
1867
|
+
log(` ${c.dim("would link")} ${linkPath} \u2192 ${relTarget}`);
|
|
1868
|
+
return;
|
|
1869
|
+
}
|
|
1870
|
+
fs.mkdirSync(path.dirname(linkPath), { recursive: true });
|
|
1871
|
+
let existingStat = null;
|
|
1872
|
+
try {
|
|
1873
|
+
existingStat = fs.lstatSync(linkPath);
|
|
1874
|
+
} catch {
|
|
1875
|
+
}
|
|
1876
|
+
if (existingStat) {
|
|
1877
|
+
if (existingStat.isSymbolicLink()) {
|
|
1878
|
+
if (fs.readlinkSync(linkPath) === relTarget) return;
|
|
1879
|
+
fs.unlinkSync(linkPath);
|
|
1880
|
+
} else if (existingStat.isDirectory()) {
|
|
1881
|
+
fs.rmSync(linkPath, { recursive: true });
|
|
1882
|
+
log(` ${c.yellow("\u2197")} migrated ${c.dim(linkPath)} (replaced directory with symlink)`);
|
|
1883
|
+
} else {
|
|
1884
|
+
fs.unlinkSync(linkPath);
|
|
1885
|
+
}
|
|
1886
|
+
}
|
|
1887
|
+
fs.symlinkSync(relTarget, linkPath);
|
|
1888
|
+
log(` ${c.dim("\u21E2")} ${linkPath}`);
|
|
1889
|
+
}
|
|
1890
|
+
function installSkill(root, mode, skillName, files, dryRun, c, log) {
|
|
1891
|
+
const home = os.homedir();
|
|
1892
|
+
if (mode === "custom") {
|
|
1893
|
+
for (const { fileName, content } of files) {
|
|
1894
|
+
const destPath = path.join(root, skillName, fileName);
|
|
1895
|
+
if (dryRun) {
|
|
1896
|
+
log(` ${c.dim("would write")} ${destPath}`);
|
|
1897
|
+
} else {
|
|
1898
|
+
fs.mkdirSync(path.dirname(destPath), { recursive: true });
|
|
1899
|
+
fs.writeFileSync(destPath, content, "utf-8");
|
|
1900
|
+
log(` ${c.green("\u2713")} ${destPath}`);
|
|
1901
|
+
}
|
|
1902
|
+
}
|
|
1903
|
+
return [];
|
|
1904
|
+
}
|
|
1905
|
+
for (const { fileName, content } of files) {
|
|
1906
|
+
const destPath = path.join(root, AGENTS_STORE, skillName, fileName);
|
|
1907
|
+
if (dryRun) {
|
|
1908
|
+
log(` ${c.dim("would write")} ${destPath}`);
|
|
1909
|
+
} else {
|
|
1910
|
+
fs.mkdirSync(path.dirname(destPath), { recursive: true });
|
|
1911
|
+
fs.writeFileSync(destPath, content, "utf-8");
|
|
1912
|
+
log(` ${c.green("\u2713")} ${destPath}`);
|
|
1913
|
+
}
|
|
1914
|
+
}
|
|
1915
|
+
const linkedAgents = [];
|
|
1916
|
+
for (const agentSkillsDir of AGENT_SYMLINK_DIRS) {
|
|
1917
|
+
const agentRootDir = agentSkillsDir.split("/")[0];
|
|
1918
|
+
const isClaude = agentRootDir === ".claude";
|
|
1919
|
+
if (!isClaude && !fs.existsSync(path.join(home, agentRootDir))) continue;
|
|
1920
|
+
const linkPath = path.join(root, agentSkillsDir, skillName);
|
|
1921
|
+
ensureSymlink(linkPath, skillName, dryRun, c, log);
|
|
1922
|
+
linkedAgents.push(agentRootDir);
|
|
1923
|
+
}
|
|
1924
|
+
return linkedAgents;
|
|
1925
|
+
}
|
|
1823
1926
|
async function pullSkillsAction(skillName, options, ctx) {
|
|
1824
1927
|
try {
|
|
1825
|
-
const
|
|
1928
|
+
const { mode, root } = resolveMode(options);
|
|
1929
|
+
const useColor = !ctx.noColor && process.stdout.isTTY;
|
|
1930
|
+
const c = makeColors(useColor);
|
|
1931
|
+
const log = ctx.json ? (_msg) => {
|
|
1932
|
+
} : (msg) => console.log(msg);
|
|
1826
1933
|
const result = await listNotes(
|
|
1827
1934
|
{ note_type: "skill", limit: 200 },
|
|
1828
1935
|
{ pin: ctx.pin }
|
|
1829
1936
|
);
|
|
1830
1937
|
let skills = result.notes;
|
|
1831
1938
|
if (skillName) {
|
|
1832
|
-
skills = skills.filter((n) =>
|
|
1833
|
-
const prefix = n.title.split("/")[0];
|
|
1834
|
-
return prefix === skillName;
|
|
1835
|
-
});
|
|
1939
|
+
skills = skills.filter((n) => n.title.split("/")[0] === skillName);
|
|
1836
1940
|
if (skills.length === 0) {
|
|
1837
1941
|
console.error(pc11.red(`No skill found with name "${skillName}"`));
|
|
1838
1942
|
process.exit(1);
|
|
1839
1943
|
}
|
|
1840
1944
|
}
|
|
1841
1945
|
if (skills.length === 0) {
|
|
1842
|
-
console.log(
|
|
1946
|
+
if (!ctx.json) console.log(c.dim("No skills to pull."));
|
|
1947
|
+
if (ctx.json) outputJson({ pulled: 0, skipped: 0, mode, root, skills: {} });
|
|
1843
1948
|
return;
|
|
1844
1949
|
}
|
|
1845
|
-
|
|
1846
|
-
let skipped = 0;
|
|
1950
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
1847
1951
|
for (const note of skills) {
|
|
1848
1952
|
const slashIdx = note.title.indexOf("/");
|
|
1849
1953
|
if (slashIdx <= 0) {
|
|
1850
|
-
|
|
1851
|
-
skipped++;
|
|
1954
|
+
if (!ctx.json) console.error(`Skipping "${note.title}" (invalid title format, expected "name/file")`);
|
|
1852
1955
|
continue;
|
|
1853
1956
|
}
|
|
1854
|
-
const
|
|
1957
|
+
const sName = note.title.slice(0, slashIdx);
|
|
1855
1958
|
const fileName = note.title.slice(slashIdx + 1);
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1959
|
+
if (!grouped.has(sName)) grouped.set(sName, []);
|
|
1960
|
+
grouped.get(sName).push({ fileName, noteId: note.id });
|
|
1961
|
+
}
|
|
1962
|
+
let totalPulled = 0;
|
|
1963
|
+
let totalSkipped = 0;
|
|
1964
|
+
const jsonResults = {};
|
|
1965
|
+
for (const [sName, fileEntries] of grouped) {
|
|
1966
|
+
log(`
|
|
1967
|
+
${c.bold(sName)}`);
|
|
1968
|
+
const files = [];
|
|
1969
|
+
for (const { fileName, noteId } of fileEntries) {
|
|
1970
|
+
const noteData = await getNote(noteId, { pin: ctx.pin });
|
|
1971
|
+
const content = noteData.latest_snippet?.content;
|
|
1972
|
+
if (!content) {
|
|
1973
|
+
if (!ctx.json) console.error(` Skipping "${sName}/${fileName}" (no snippet content)`);
|
|
1974
|
+
totalSkipped++;
|
|
1975
|
+
continue;
|
|
1976
|
+
}
|
|
1977
|
+
files.push({ fileName, content });
|
|
1863
1978
|
}
|
|
1979
|
+
if (files.length === 0) continue;
|
|
1980
|
+
const linkedAgents = installSkill(root, mode, sName, files, options.dryRun ?? false, c, log);
|
|
1981
|
+
totalPulled += files.length;
|
|
1982
|
+
jsonResults[sName] = {
|
|
1983
|
+
files: files.map((f) => f.fileName),
|
|
1984
|
+
agents: linkedAgents
|
|
1985
|
+
};
|
|
1986
|
+
}
|
|
1987
|
+
if (!ctx.json) {
|
|
1988
|
+
console.log("");
|
|
1864
1989
|
if (options.dryRun) {
|
|
1865
|
-
console.log(
|
|
1866
|
-
|
|
1867
|
-
|
|
1990
|
+
console.log(c.dim(`Dry run: ${totalPulled} file(s) would be pulled, ${totalSkipped} skipped`));
|
|
1991
|
+
} else {
|
|
1992
|
+
const scopeLabel = mode === "custom" ? root : mode === "project" ? "project (.agents/skills/)" : "global (~/.agents/skills/)";
|
|
1993
|
+
console.log(
|
|
1994
|
+
`Pulled ${totalPulled} file(s) \u2014 ${scopeLabel}` + (totalSkipped > 0 ? `, ${totalSkipped} skipped` : "")
|
|
1995
|
+
);
|
|
1868
1996
|
}
|
|
1869
|
-
const dir = path.dirname(filePath);
|
|
1870
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
1871
|
-
fs.writeFileSync(filePath, content, "utf-8");
|
|
1872
|
-
console.log(`${pc11.green("\u2713")} ${filePath}`);
|
|
1873
|
-
pulled++;
|
|
1874
1997
|
}
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
console.log(pc11.dim(`Dry run: ${pulled} file(s) would be written, ${skipped} skipped`));
|
|
1878
|
-
} else {
|
|
1879
|
-
console.log(`Pulled ${pulled} file(s) to ${baseDir}` + (skipped > 0 ? `, ${skipped} skipped` : ""));
|
|
1998
|
+
if (ctx.json) {
|
|
1999
|
+
outputJson({ pulled: totalPulled, skipped: totalSkipped, mode, root, skills: jsonResults });
|
|
1880
2000
|
}
|
|
1881
2001
|
} catch (error) {
|
|
1882
2002
|
handleError(error, ctx.noColor);
|
|
@@ -1983,16 +2103,20 @@ var skillsCommand = new Command8("skills").description("Manage agent skills (syn
|
|
|
1983
2103
|
"after",
|
|
1984
2104
|
`
|
|
1985
2105
|
Examples:
|
|
1986
|
-
$ pnote skills
|
|
1987
|
-
$ pnote skills pull
|
|
1988
|
-
$ pnote skills pull myskill
|
|
1989
|
-
$ pnote skills
|
|
2106
|
+
$ pnote skills List all skills in cloud
|
|
2107
|
+
$ pnote skills pull Pull all skills globally (~/.agents/skills/ + symlinks)
|
|
2108
|
+
$ pnote skills pull myskill Pull a specific skill globally
|
|
2109
|
+
$ pnote skills pull --project Pull project-level (.agents/skills/ + .claude/skills/ etc.)
|
|
2110
|
+
$ pnote skills pull --dry-run Preview without writing files
|
|
2111
|
+
$ pnote skills push ./my-skill Upload a local skill directory
|
|
1990
2112
|
|
|
1991
|
-
Skills are notes with type "skill" and title
|
|
1992
|
-
|
|
2113
|
+
Skills are stored as notes with type "skill" and title "skill-name/filename.md".
|
|
2114
|
+
Pull writes to the canonical .agents/skills/ store and symlinks into each installed
|
|
2115
|
+
agent's directory (.claude/skills/, .windsurf/skills/, .augment/skills/, etc.),
|
|
2116
|
+
matching the layout used by npx skills for seamless coexistence.
|
|
1993
2117
|
`
|
|
1994
2118
|
);
|
|
1995
|
-
skillsCommand.command("pull").description("Download skills from cloud to local (
|
|
2119
|
+
skillsCommand.command("pull").description("Download skills from cloud to local (global by default)").argument("[skill-name]", "Specific skill to pull (pulls all if omitted)").option("--project", "Install project-level (./agents/skills/ + .claude/skills/ etc.)").option("--dir <path>", "Custom output directory \u2014 direct write, no symlinks").option("--dry-run", "Show what would be downloaded without writing files").action(async (skillName, options, cmd) => {
|
|
1996
2120
|
const globalOpts = cmd.parent?.parent?.opts() || {};
|
|
1997
2121
|
const ctx = await buildContext6(globalOpts);
|
|
1998
2122
|
await pullSkillsAction(skillName, options, ctx);
|
|
@@ -2003,10 +2127,170 @@ skillsCommand.command("push").description("Upload a local skill directory to clo
|
|
|
2003
2127
|
await pushSkillsAction(dir, options, ctx);
|
|
2004
2128
|
});
|
|
2005
2129
|
|
|
2130
|
+
// src/commands/pin/index.ts
|
|
2131
|
+
import { Command as Command9 } from "commander";
|
|
2132
|
+
|
|
2133
|
+
// src/commands/pin/statusAction.ts
|
|
2134
|
+
import pc13 from "picocolors";
|
|
2135
|
+
async function pinStatusAction(ctx) {
|
|
2136
|
+
try {
|
|
2137
|
+
const result = await checkPinStatus({ pin: ctx.pin });
|
|
2138
|
+
if (ctx.json) {
|
|
2139
|
+
outputJson({
|
|
2140
|
+
...result,
|
|
2141
|
+
pnote_pin_env_set: !!process.env.PNOTE_PIN,
|
|
2142
|
+
pin_cached: !!getCachedPin()
|
|
2143
|
+
});
|
|
2144
|
+
return;
|
|
2145
|
+
}
|
|
2146
|
+
const useColor = !ctx.noColor && process.stdout.isTTY;
|
|
2147
|
+
const c = {
|
|
2148
|
+
green: useColor ? pc13.green : (s) => s,
|
|
2149
|
+
red: useColor ? pc13.red : (s) => s,
|
|
2150
|
+
yellow: useColor ? pc13.yellow : (s) => s,
|
|
2151
|
+
dim: useColor ? pc13.dim : (s) => s,
|
|
2152
|
+
bold: useColor ? pc13.bold : (s) => s,
|
|
2153
|
+
magenta: useColor ? pc13.magenta : (s) => s
|
|
2154
|
+
};
|
|
2155
|
+
const yes = c.green("\u2713");
|
|
2156
|
+
const no = c.red("\u2717");
|
|
2157
|
+
console.log(c.bold("PIN Status"));
|
|
2158
|
+
console.log("");
|
|
2159
|
+
const hasPin = result.user_has_pin_setup;
|
|
2160
|
+
console.log(` Account PIN set: ${hasPin ? yes : no}`);
|
|
2161
|
+
if (result.pin_hint) {
|
|
2162
|
+
console.log(` PIN hint: ${c.dim(result.pin_hint)}`);
|
|
2163
|
+
}
|
|
2164
|
+
console.log(` Protected notes: ${c.magenta(String(result.protected_notes_count))}`);
|
|
2165
|
+
console.log("");
|
|
2166
|
+
console.log(c.bold("PIN Sources (CLI)"));
|
|
2167
|
+
console.log("");
|
|
2168
|
+
const envSet = !!process.env.PNOTE_PIN;
|
|
2169
|
+
console.log(` PNOTE_PIN env: ${envSet ? yes : no}${envSet ? c.dim(" (recommended)") : ""}`);
|
|
2170
|
+
const cached = !!getCachedPin();
|
|
2171
|
+
console.log(` Session cache: ${cached ? yes + c.dim(" (active, <5min)") : no}`);
|
|
2172
|
+
if (result.pin_provided) {
|
|
2173
|
+
const valid = result.pin_valid;
|
|
2174
|
+
console.log(` Provided PIN valid: ${valid ? yes : no}`);
|
|
2175
|
+
} else {
|
|
2176
|
+
console.log(` Provided PIN: ${c.dim("not provided")}`);
|
|
2177
|
+
}
|
|
2178
|
+
console.log("");
|
|
2179
|
+
const canAccess = result.can_access_protected;
|
|
2180
|
+
if (canAccess) {
|
|
2181
|
+
console.log(yes + " " + c.green("Can access protected notes"));
|
|
2182
|
+
} else if (!hasPin) {
|
|
2183
|
+
console.log(c.dim(" No protected notes \u2014 PIN not configured on account"));
|
|
2184
|
+
} else {
|
|
2185
|
+
console.log(no + " " + c.red("Cannot access protected notes"));
|
|
2186
|
+
console.log("");
|
|
2187
|
+
console.log(c.dim(" Set PIN via: export PNOTE_PIN=<pin>"));
|
|
2188
|
+
console.log(c.dim(" Or use: pnote notes get <id> -p <pin>"));
|
|
2189
|
+
console.log(c.dim(" Or use: echo <pin> | pnote notes get <id> --pin-stdin"));
|
|
2190
|
+
}
|
|
2191
|
+
} catch (error) {
|
|
2192
|
+
handleError(error, ctx.noColor);
|
|
2193
|
+
}
|
|
2194
|
+
}
|
|
2195
|
+
|
|
2196
|
+
// src/commands/pin/listAction.ts
|
|
2197
|
+
import pc14 from "picocolors";
|
|
2198
|
+
async function pinListAction(options, ctx) {
|
|
2199
|
+
try {
|
|
2200
|
+
const result = await listProtectedNotes(
|
|
2201
|
+
{
|
|
2202
|
+
limit: options.limit ? parseInt(options.limit, 10) : 50,
|
|
2203
|
+
offset: options.offset ? parseInt(options.offset, 10) : void 0
|
|
2204
|
+
},
|
|
2205
|
+
{ pin: ctx.pin }
|
|
2206
|
+
);
|
|
2207
|
+
if (ctx.json) {
|
|
2208
|
+
outputJson(result);
|
|
2209
|
+
return;
|
|
2210
|
+
}
|
|
2211
|
+
const useColor = !ctx.noColor && process.stdout.isTTY;
|
|
2212
|
+
const c = {
|
|
2213
|
+
dim: useColor ? pc14.dim : (s) => s,
|
|
2214
|
+
bold: useColor ? pc14.bold : (s) => s,
|
|
2215
|
+
cyan: useColor ? pc14.cyan : (s) => s,
|
|
2216
|
+
yellow: useColor ? pc14.yellow : (s) => s,
|
|
2217
|
+
magenta: useColor ? pc14.magenta : (s) => s
|
|
2218
|
+
};
|
|
2219
|
+
const isHuman = !ctx.json && !ctx.plain && process.stdout.isTTY;
|
|
2220
|
+
if (result.notes.length === 0) {
|
|
2221
|
+
console.log(c.dim("No protected notes."));
|
|
2222
|
+
return;
|
|
2223
|
+
}
|
|
2224
|
+
if (isHuman) {
|
|
2225
|
+
console.log(c.bold(`Protected Notes (${result.count})`));
|
|
2226
|
+
if (result.hint) {
|
|
2227
|
+
console.log(c.dim(`PIN hint: ${result.hint}`));
|
|
2228
|
+
}
|
|
2229
|
+
if (!result.can_decrypt) {
|
|
2230
|
+
console.log(c.dim("Note: Content not shown \u2014 set PNOTE_PIN to access content."));
|
|
2231
|
+
}
|
|
2232
|
+
console.log("");
|
|
2233
|
+
const pad2 = (s, w) => s.length >= w ? s.slice(0, w) : s + " ".repeat(w - s.length);
|
|
2234
|
+
const truncate2 = (s, w) => s.length <= w ? s : s.slice(0, w - 1) + "\u2026";
|
|
2235
|
+
console.log(
|
|
2236
|
+
" " + c.dim(pad2("ID", 10)) + " " + c.dim(pad2("TITLE", 30)) + " " + c.dim(pad2("TAGS", 20)) + " " + c.dim("PROTECTED")
|
|
2237
|
+
);
|
|
2238
|
+
for (const note of result.notes) {
|
|
2239
|
+
const date = new Date(note.protected_at);
|
|
2240
|
+
const updated = date.toLocaleDateString();
|
|
2241
|
+
const pinned = note.pinned ? c.yellow("* ") : " ";
|
|
2242
|
+
console.log(
|
|
2243
|
+
pinned + pad2(note.id.slice(0, 8), 10) + " " + pad2(truncate2(note.title, 30), 30) + " " + pad2(truncate2(note.tags.join(", ") || "-", 20), 20) + " " + c.magenta(updated)
|
|
2244
|
+
);
|
|
2245
|
+
}
|
|
2246
|
+
} else {
|
|
2247
|
+
for (const note of result.notes) {
|
|
2248
|
+
console.log([note.id, note.title, note.tags.join(","), note.protected_at].join(" "));
|
|
2249
|
+
}
|
|
2250
|
+
}
|
|
2251
|
+
} catch (error) {
|
|
2252
|
+
handleError(error, ctx.noColor);
|
|
2253
|
+
}
|
|
2254
|
+
}
|
|
2255
|
+
|
|
2256
|
+
// src/commands/pin/index.ts
|
|
2257
|
+
async function buildContext7(globalOpts) {
|
|
2258
|
+
const pin = await resolvePin({
|
|
2259
|
+
pinArg: globalOpts.pin,
|
|
2260
|
+
pinFromStdin: globalOpts.pinStdin,
|
|
2261
|
+
skipPrompt: true
|
|
2262
|
+
});
|
|
2263
|
+
return {
|
|
2264
|
+
json: globalOpts.json ?? false,
|
|
2265
|
+
noColor: globalOpts.noColor ?? false,
|
|
2266
|
+
plain: globalOpts.plain ?? false,
|
|
2267
|
+
pin: pin ?? void 0
|
|
2268
|
+
};
|
|
2269
|
+
}
|
|
2270
|
+
var pinCommand = new Command9("pin").description("Manage PIN protection for notes").addHelpText(
|
|
2271
|
+
"after",
|
|
2272
|
+
`
|
|
2273
|
+
Examples:
|
|
2274
|
+
$ pnote pin status Check PIN configuration and access
|
|
2275
|
+
$ pnote pin list List all protected notes (metadata only)
|
|
2276
|
+
$ PNOTE_PIN=1234 pnote pin status Check with PIN provided via env
|
|
2277
|
+
`
|
|
2278
|
+
);
|
|
2279
|
+
pinCommand.command("status").description("Check PIN configuration status and access to protected notes").action(async (_options, cmd) => {
|
|
2280
|
+
const globalOpts = cmd.parent?.parent?.opts() || {};
|
|
2281
|
+
const ctx = await buildContext7(globalOpts);
|
|
2282
|
+
await pinStatusAction(ctx);
|
|
2283
|
+
});
|
|
2284
|
+
pinCommand.command("list").description("List all PIN-protected notes (metadata only, no content)").option("--limit <n>", "Maximum number of results", "50").option("--offset <n>", "Skip N results (for pagination)").action(async (options, cmd) => {
|
|
2285
|
+
const globalOpts = cmd.parent?.parent?.opts() || {};
|
|
2286
|
+
const ctx = await buildContext7(globalOpts);
|
|
2287
|
+
await pinListAction(options, ctx);
|
|
2288
|
+
});
|
|
2289
|
+
|
|
2006
2290
|
// src/lib/update-check.ts
|
|
2007
|
-
import { existsSync as
|
|
2291
|
+
import { existsSync as existsSync5, readFileSync as readFileSync4, writeFileSync as writeFileSync4, mkdirSync as mkdirSync3 } from "fs";
|
|
2008
2292
|
import { join as join5 } from "path";
|
|
2009
|
-
import
|
|
2293
|
+
import pc15 from "picocolors";
|
|
2010
2294
|
var CACHE_FILE = "update-check.json";
|
|
2011
2295
|
var CHECK_INTERVAL = 60 * 60 * 1e3;
|
|
2012
2296
|
var FETCH_TIMEOUT = 2e3;
|
|
@@ -2016,7 +2300,7 @@ function getCachePath() {
|
|
|
2016
2300
|
function readCache() {
|
|
2017
2301
|
try {
|
|
2018
2302
|
const path3 = getCachePath();
|
|
2019
|
-
if (!
|
|
2303
|
+
if (!existsSync5(path3)) return null;
|
|
2020
2304
|
return JSON.parse(readFileSync4(path3, "utf-8"));
|
|
2021
2305
|
} catch {
|
|
2022
2306
|
return null;
|
|
@@ -2025,7 +2309,7 @@ function readCache() {
|
|
|
2025
2309
|
function writeCache(cache) {
|
|
2026
2310
|
try {
|
|
2027
2311
|
const dir = getConfigDir();
|
|
2028
|
-
if (!
|
|
2312
|
+
if (!existsSync5(dir)) {
|
|
2029
2313
|
mkdirSync3(dir, { recursive: true, mode: 448 });
|
|
2030
2314
|
}
|
|
2031
2315
|
writeFileSync4(getCachePath(), JSON.stringify(cache), "utf-8");
|
|
@@ -2055,9 +2339,9 @@ function isOlderVersion(current, latest) {
|
|
|
2055
2339
|
}
|
|
2056
2340
|
function printNotice(current, latest, packageName) {
|
|
2057
2341
|
const useColor = process.stderr.isTTY && !process.env.NO_COLOR && process.env.TERM !== "dumb";
|
|
2058
|
-
const yellow = useColor ?
|
|
2059
|
-
const bold = useColor ?
|
|
2060
|
-
const dim = useColor ?
|
|
2342
|
+
const yellow = useColor ? pc15.yellow : (s) => s;
|
|
2343
|
+
const bold = useColor ? pc15.bold : (s) => s;
|
|
2344
|
+
const dim = useColor ? pc15.dim : (s) => s;
|
|
2061
2345
|
const msg = `Update available: ${dim(current)} \u2192 ${bold(latest)}`;
|
|
2062
2346
|
const cmd = `Run ${bold(`npm i -g ${packageName}`)} to update`;
|
|
2063
2347
|
const lines = [msg, cmd];
|
|
@@ -2103,8 +2387,8 @@ async function checkAndNotifyUpdate(currentVersion, packageName) {
|
|
|
2103
2387
|
}
|
|
2104
2388
|
|
|
2105
2389
|
// src/index.ts
|
|
2106
|
-
var program = new
|
|
2107
|
-
program.name("pnote").description("pnote - The PromptNote CLI").version("0.
|
|
2390
|
+
var program = new Command10();
|
|
2391
|
+
program.name("pnote").description("pnote - The PromptNote CLI").version("0.4.1", "-V, --version", "Show version number").option("--json", "Output as JSON (for scripting)").option("--no-color", "Disable colored output").option("--plain", "Force plain text output (no formatting)").option("-p, --pin <pin>", "PIN for accessing protected notes").option("--pin-stdin", "Read PIN from stdin (first line only)").configureHelp({
|
|
2108
2392
|
sortSubcommands: true,
|
|
2109
2393
|
sortOptions: true
|
|
2110
2394
|
}).addHelpText(
|
|
@@ -2134,9 +2418,10 @@ program.addCommand(snippetCommand);
|
|
|
2134
2418
|
program.addCommand(tagsCommand);
|
|
2135
2419
|
program.addCommand(searchCommand);
|
|
2136
2420
|
program.addCommand(shareCommand);
|
|
2421
|
+
program.addCommand(pinCommand);
|
|
2137
2422
|
program.addCommand(skillsCommand);
|
|
2138
2423
|
program.hook("preAction", async () => {
|
|
2139
|
-
await checkAndNotifyUpdate("0.
|
|
2424
|
+
await checkAndNotifyUpdate("0.4.1", "pnote");
|
|
2140
2425
|
});
|
|
2141
2426
|
process.on("SIGINT", () => {
|
|
2142
2427
|
console.error("\nInterrupted");
|