innernote 0.2.0 → 0.3.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/README.md CHANGED
@@ -78,18 +78,28 @@ any time.
78
78
 
79
79
  ## Commands
80
80
 
81
+ The table below is asserted by `cli/src/parity.test.ts`: a command missing
82
+ from it fails the suite, so what you read here is what the binary has.
83
+
81
84
  | Command | What it does |
82
85
  | --- | --- |
83
86
  | `login [code]` | connect this machine |
84
87
  | `logout` | forget the token stored here |
85
- | `whoami` | show which account is connected |
88
+ | `whoami` | show which account is connected, and what this key can do |
89
+ | `context` | what you post about: your pillars and your series |
86
90
  | `capture <text>` | save an idea to your inbox |
87
91
  | `write [topic]` | draft a post in your voice |
92
+ | `shape <shape...>` | push a post shorter, punchier, warmer |
93
+ | `ask <anything>` | talk to the post in your own words |
94
+ | `save` | keep the draft you are holding |
95
+ | `show` | reprint the post you are holding |
96
+ | `drop` | put down whatever you are holding |
97
+ | `open <n or id>` | pull a post out of the last list and work on it |
88
98
  | `ideas [status]` | list what you have captured |
89
- | `drafts [status]` | list your posts, with their ids |
99
+ | `drafts [status]` | list your posts, numbered |
90
100
  | `week` | what is scheduled for the next seven days |
91
- | `shape <shape...>` | push a post shorter, punchier, warmer (reads stdin) |
92
101
  | `queue <post-id>` | schedule a post into your next open slot |
102
+ | `unqueue <post-id>` | pull it back out before it goes live |
93
103
 
94
104
  ### write
95
105
 
package/dist/index.js CHANGED
@@ -609,6 +609,42 @@ async function week(args) {
609
609
  }
610
610
  return 0;
611
611
  }
612
+ async function context(args) {
613
+ const { json } = parseFlags(args);
614
+ const [pillars, series] = await Promise.all([
615
+ request("/api/pillars"),
616
+ request("/api/campaigns")
617
+ ]);
618
+ if (json) {
619
+ out(JSON.stringify({ pillars: pillars.pillars, series: series.campaigns }, null, 2));
620
+ return 0;
621
+ }
622
+ out();
623
+ if (pillars.pillars.length) {
624
+ out(` ${bold2("What you post about")}`);
625
+ for (const p of pillars.pillars) {
626
+ out(` ${caramel(p.name)}${typeof p.weight === "number" ? dim2(` ${p.weight}%`) : ""}`);
627
+ }
628
+ } else {
629
+ out(` ${dim2("No content pillars yet. Set them in Settings.")}`);
630
+ }
631
+ out();
632
+ if (series.campaigns.length) {
633
+ out(` ${bold2("Series")}${dim2(" write into one with")} ${caramel('write --series "<name>"')}`);
634
+ for (const c of series.campaigns) {
635
+ const count = typeof c.postCount === "number" && c.postCount > 0 ? dim2(` ${c.postCount} ${c.postCount === 1 ? "post" : "posts"}`) : "";
636
+ out(` ${caramel(c.name)}${count}`);
637
+ if (c.brief)
638
+ out(` ${dim2(oneLine(c.brief, 64))}`);
639
+ if (c.goal)
640
+ out(` ${dim2(`goal: ${oneLine(c.goal, 58)}`)}`);
641
+ }
642
+ } else {
643
+ out(` ${dim2("No series running.")}`);
644
+ }
645
+ out();
646
+ return 0;
647
+ }
612
648
 
613
649
  // src/commands/read.ts
614
650
  function parseFlags2(args) {
@@ -1078,6 +1114,23 @@ async function drop() {
1078
1114
  note(dim2("`write` to start something, or `drafts` to pick one up"));
1079
1115
  return 0;
1080
1116
  }
1117
+ async function unqueue(args) {
1118
+ const { json, rest } = parseFlags2(args);
1119
+ const ref = rest[0] ? resolveRef(rest[0]) : null;
1120
+ if (!ref) {
1121
+ fail("Which post?");
1122
+ note(dim2(`${CMD} unqueue <post-id> (\`${CMD} week\` shows what is queued)`));
1123
+ return 1;
1124
+ }
1125
+ const result = await request(`/api/linkedin/schedule/${encodeURIComponent(ref.id)}`, { method: "DELETE" });
1126
+ if (json) {
1127
+ out(JSON.stringify(result, null, 2));
1128
+ return 0;
1129
+ }
1130
+ ok("Pulled back out of the queue. It is a draft again.");
1131
+ note(dim2("`drafts` to find it, `queue` to send it back"));
1132
+ return 0;
1133
+ }
1081
1134
 
1082
1135
  // src/ui.ts
1083
1136
  var useColor2 = process.stdout.isTTY === true && !process.env.NO_COLOR && process.env.TERM !== "dumb";
@@ -1316,6 +1369,18 @@ var COMMANDS = {
1316
1369
  usage: "innernote week",
1317
1370
  detail: "Seven days across, so the gaps are visible before you read a word. Published is filled, scheduled is open."
1318
1371
  },
1372
+ unqueue: {
1373
+ summary: "pull a post back out of the queue before it goes live",
1374
+ usage: "innernote unqueue <post-id>",
1375
+ detail: "The undo for queue, while there is still time. The post returns to your drafts and nothing goes live. Once it has been published this refuses, because a live post cannot be unsent this way.",
1376
+ examples: ["innernote unqueue p17abc...", "innernote unqueue 3"]
1377
+ },
1378
+ context: {
1379
+ summary: "what you post about: your pillars and your series",
1380
+ usage: "innernote context",
1381
+ detail: "Your standing context: content pillars with their weights, and every running series with what it is about and what it is for. This is the thing to read before deciding what to write, and the thing an agent should read first for the same reason. --json prints the raw payload for scripts and models.",
1382
+ examples: ["innernote context", "innernote context --json"]
1383
+ },
1319
1384
  queue: {
1320
1385
  summary: "schedule a post into your next open slot",
1321
1386
  usage: "innernote queue <post-id>",
@@ -1433,7 +1498,7 @@ function suggest(state) {
1433
1498
  // package.json
1434
1499
  var package_default = {
1435
1500
  name: "innernote",
1436
- version: "0.2.0",
1501
+ version: "0.3.1",
1437
1502
  description: "Write LinkedIn posts in your voice, from the terminal.",
1438
1503
  type: "module",
1439
1504
  bin: {
@@ -1441,6 +1506,7 @@ var package_default = {
1441
1506
  },
1442
1507
  files: [
1443
1508
  "dist",
1509
+ "skills",
1444
1510
  "README.md"
1445
1511
  ],
1446
1512
  scripts: {
@@ -1654,6 +1720,18 @@ var COMMANDS2 = {
1654
1720
  usage: "innernote week",
1655
1721
  detail: "Seven days across, so the gaps are visible before you read a word. Published is filled, scheduled is open."
1656
1722
  },
1723
+ unqueue: {
1724
+ summary: "pull a post back out of the queue before it goes live",
1725
+ usage: "innernote unqueue <post-id>",
1726
+ detail: "The undo for queue, while there is still time. The post returns to your drafts and nothing goes live. Once it has been published this refuses, because a live post cannot be unsent this way.",
1727
+ examples: ["innernote unqueue p17abc...", "innernote unqueue 3"]
1728
+ },
1729
+ context: {
1730
+ summary: "what you post about: your pillars and your series",
1731
+ usage: "innernote context",
1732
+ detail: "Your standing context: content pillars with their weights, and every running series with what it is about and what it is for. This is the thing to read before deciding what to write, and the thing an agent should read first for the same reason. --json prints the raw payload for scripts and models.",
1733
+ examples: ["innernote context", "innernote context --json"]
1734
+ },
1657
1735
  queue: {
1658
1736
  summary: "schedule a post into your next open slot",
1659
1737
  usage: "innernote queue <post-id>",
@@ -1914,8 +1992,8 @@ function whatThereIs() {
1914
1992
  ];
1915
1993
  const groups = [
1916
1994
  ["Writing", ["write", "shape", "ask", "save", "capture"]],
1917
- ["Looking", ["show", "ideas", "drafts", "open", "week", "whoami"]],
1918
- ["Shipping", ["queue"]],
1995
+ ["Looking", ["show", "ideas", "drafts", "open", "week", "context", "whoami"]],
1996
+ ["Shipping", ["queue", "unqueue"]],
1919
1997
  ["Connection", ["login", "logout"]]
1920
1998
  ];
1921
1999
  for (const [title, cmds] of groups) {
@@ -1944,8 +2022,8 @@ function commandList() {
1944
2022
  const groups = [
1945
2023
  ["Getting connected", ["login", "logout", "whoami"]],
1946
2024
  ["Writing", ["write", "shape", "ask", "save", "capture"]],
1947
- ["Looking", ["show", "ideas", "drafts", "open", "week"]],
1948
- ["Shipping", ["queue"]]
2025
+ ["Looking", ["show", "ideas", "drafts", "open", "week", "context"]],
2026
+ ["Shipping", ["queue", "unqueue"]]
1949
2027
  ];
1950
2028
  const lines = [];
1951
2029
  for (const [title, names] of groups) {
@@ -1970,6 +2048,7 @@ var HELP = () => [
1970
2048
  ].join(`
1971
2049
  `);
1972
2050
  var COMMANDS3 = {
2051
+ context,
1973
2052
  login,
1974
2053
  logout: () => logout(),
1975
2054
  whoami,
@@ -1984,7 +2063,8 @@ var COMMANDS3 = {
1984
2063
  drafts,
1985
2064
  open,
1986
2065
  week,
1987
- queue
2066
+ queue,
2067
+ unqueue
1988
2068
  };
1989
2069
  async function main() {
1990
2070
  const [, , command, ...args] = process.argv;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "innernote",
3
- "version": "0.2.0",
3
+ "version": "0.3.1",
4
4
  "description": "Write LinkedIn posts in your voice, from the terminal.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -8,6 +8,7 @@
8
8
  },
9
9
  "files": [
10
10
  "dist",
11
+ "skills",
11
12
  "README.md"
12
13
  ],
13
14
  "scripts": {
@@ -0,0 +1,125 @@
1
+ ---
2
+ name: innernote-cli
3
+ description: Drive the innernote CLI (`npx innernote`) to help someone write, save and schedule LinkedIn posts in their own voice from the terminal. Use this whenever the user mentions innernote, asks to draft or queue a LinkedIn post from the command line, wants their posting week or captured ideas checked, or asks you to capture a thought for later, even if they do not name the CLI. Also use it before touching any `innernote` command in a script.
4
+ ---
5
+
6
+ # Driving the innernote CLI
7
+
8
+ innernote holds this person's writing voice: how they write, what they post
9
+ about, and their posting schedule. The CLI is a thin client over the same
10
+ service the app uses, so a draft made here sounds like THEM, not like you,
11
+ and everything you save appears in their app.
12
+
13
+ ## The contract, before any command
14
+
15
+ - **stdout is data, stderr is narration.** The post text, JSON and ids go to
16
+ stdout; spinners, notes and errors go to stderr. Pipe stdout with
17
+ confidence, and never parse the human-facing view: every read command takes
18
+ `--json`, which prints the API payload and nothing else.
19
+ - **Exit codes are the protocol.** `0` worked. `1` failed, reason on stderr.
20
+ `2` not connected: stop and ask the user to run `npx innernote login`.
21
+ `3` the account cannot do this right now: a paywall or a spent allowance,
22
+ never a bug. Only `1` is worth debugging.
23
+ - **A refusal is a sentence for the user, not an obstacle for you.** When a
24
+ command refuses (a limit, a missing cadence, no such post), the message is
25
+ already written for the person. Show it to them verbatim and stop. Do not
26
+ retry, and do not work around it.
27
+
28
+ ## Connecting
29
+
30
+ Check with `innernote whoami` (exit `2` means not connected). Connecting
31
+ needs a pairing code from the user's Settings page at
32
+ innernote.space/dashboard/settings; you cannot get one yourself. Ask them
33
+ for it, then run `innernote login THEIR-CODE`. The code works once and
34
+ expires in ten minutes. `INNERNOTE_TOKEN` in the environment overrides the
35
+ stored key, which is how CI and scripts authenticate.
36
+
37
+ ## Read before you write
38
+
39
+ Suggestions that ignore what this person posts about are generic advice,
40
+ which is the one thing innernote exists to prevent.
41
+
42
+ ```bash
43
+ innernote context --json # their content pillars and running series
44
+ innernote week --json # what is scheduled over the next seven days
45
+ innernote ideas --json # thoughts they captured and have not written
46
+ innernote drafts --json # saved posts, newest first, with ids
47
+ ```
48
+
49
+ Start with `context` and `week` when helping them decide what to write: an
50
+ empty week is a real answer, and a post should land inside a pillar or a
51
+ series they have committed to.
52
+
53
+ ## The writing loop
54
+
55
+ **Capture** the moment something is worth posting about later:
56
+
57
+ ```bash
58
+ innernote capture "their thought, in their words"
59
+ git log --oneline -20 | innernote capture # stdin works too
60
+ ```
61
+
62
+ Capture their framing, not your tidied summary. Their phrasing carries their
63
+ voice; a summary throws it away.
64
+
65
+ **Draft and keep** in one command:
66
+
67
+ ```bash
68
+ innernote write "their rough thought or topic, in their words" --save
69
+ innernote write "next part of the hiring story" --series "Build in Public" --save
70
+ ```
71
+
72
+ Pass their OWN WORDS as the argument, as fully as you have them. `write`
73
+ takes a whole messy thought, and any shaping you already know they want
74
+ (shorter, no list, warmer ending) belongs in that ask, phrased plainly.
75
+ `--series` takes the series NAME; a wrong name refuses with the real list,
76
+ so correct from that and retry once. The saved id is printed on stderr and
77
+ `innernote drafts --json` lists it first.
78
+
79
+ **Shaping an already-saved post is not yours to finish here.** Each CLI
80
+ invocation is a fresh process, so the flag form of `shape` transforms
81
+ stdin to stdout without saving:
82
+
83
+ ```bash
84
+ innernote shape shorter punchier < draft.txt # transformed text, NOT saved
85
+ ```
86
+
87
+ Use that for showing the person options. To revise and keep, fold the
88
+ direction into a fresh `write ... --save`, or hand them to the app or the
89
+ interactive session (`innernote` on its own), where reshaping a held draft
90
+ does persist.
91
+
92
+ ## Scheduling: the one action that reaches other people
93
+
94
+ `innernote queue <id>` schedules a saved post into their next open slot,
95
+ and it then goes live on LinkedIn on its own. Nobody presses anything
96
+ again, and a live post cannot be unsent.
97
+
98
+ So queue ONLY when the user has clearly told you to queue that specific
99
+ post. Never as a helpful next step, never to tidy up. If they have not said
100
+ so, tell them it is saved and stop. The undo exists while it is still
101
+ pending:
102
+
103
+ ```bash
104
+ innernote queue p17abc... # their explicit ask, and only then
105
+ innernote unqueue p17abc... # back to drafts, nothing goes out
106
+ ```
107
+
108
+ If `queue` exits `3`, their plan or allowance is the reason; show the
109
+ message, which says where to fix it, and leave the decision with them.
110
+
111
+ ## Worked example
112
+
113
+ The user says: "turn what I told you about interview homework into a post
114
+ for my build-in-public series, keep it short, and line it up for this week."
115
+
116
+ ```bash
117
+ innernote context --json # confirm the series name
118
+ innernote week --json # is there an open day?
119
+ innernote write "interview homework filters for free time, not talent. we swapped ours for a paid 40 minute working session and offer accepts went up. keep it short." --series "Build in Public" --save
120
+ innernote drafts --json # take the first id
121
+ innernote queue <that-id> # they said "line it up": that is the go-ahead
122
+ ```
123
+
124
+ Then tell them what was drafted, where it was queued, and that `unqueue`
125
+ takes it back any time before it goes live.