octwin-cli 0.6.1 → 0.7.0
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/CHANGELOG.md +540 -520
- package/dist/index.js +118 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -620,6 +620,54 @@ async function notifyIfKbStale(flags) {
|
|
|
620
620
|
}
|
|
621
621
|
catch { /* a KB check must never break the CLI */ }
|
|
622
622
|
}
|
|
623
|
+
/**
|
|
624
|
+
* Nudge (to stderr) when the platform has memos this workspace has not read — a reply to
|
|
625
|
+
* a `octwin feedback` report, or a notice we published (new capability, deprecation,
|
|
626
|
+
* breaking change).
|
|
627
|
+
*
|
|
628
|
+
* The sibling of `notifyIfKbStale`, and the same three decisions apply for the same
|
|
629
|
+
* reasons: it rides commands that ALREADY hit the platform so it costs one tiny GET on
|
|
630
|
+
* top of networked work; it never throws, because observing must not break a command;
|
|
631
|
+
* and it is **deliberately not TTY-gated**, because the primary reader of this CLI is an
|
|
632
|
+
* authoring agent whose output is piped — the one reader that cannot notice an unread
|
|
633
|
+
* reply on its own would otherwise be the only one never told.
|
|
634
|
+
*
|
|
635
|
+
* WHY THIS EXISTS AT ALL. `octwin feedback` was one-way. Measured 2026-08-18: four
|
|
636
|
+
* reports sat unread for nine days, and one author hardcoded English across 16 flows to
|
|
637
|
+
* work around a bug that had been fixed two days earlier, because nothing could tell
|
|
638
|
+
* them. A nudge that does not name the command to run does not motivate an agent, so the
|
|
639
|
+
* line always ends in `octwin memos`.
|
|
640
|
+
*
|
|
641
|
+
* Unlike the KB check there is NO local state to compare: read state is per-tenant on the
|
|
642
|
+
* platform, so an agent on a fresh machine or in a fresh container still learns about an
|
|
643
|
+
* unread memo. That is the whole reason it is not a local marker file.
|
|
644
|
+
*/
|
|
645
|
+
async function notifyIfMemosWaiting(flags) {
|
|
646
|
+
try {
|
|
647
|
+
const t = resolveTargetOrNull(flags);
|
|
648
|
+
if (!t)
|
|
649
|
+
return;
|
|
650
|
+
const { status, json } = await apiGet(`${t.url}/api/self/p/memos?meta=1`, t);
|
|
651
|
+
// Silent on ANY failure — an older platform has no such route, and a nudge is never
|
|
652
|
+
// worth a diagnostic of its own. Unlike the KB poll there is no scope to explain: the
|
|
653
|
+
// route is gated on tenant access precisely so every token can answer it.
|
|
654
|
+
if (status !== 200 || !json || typeof json !== 'object')
|
|
655
|
+
return;
|
|
656
|
+
const unread = Number(json.unread ?? 0);
|
|
657
|
+
if (!Number.isFinite(unread) || unread <= 0)
|
|
658
|
+
return;
|
|
659
|
+
const actionable = Number(json.unread_actionable ?? 0);
|
|
660
|
+
const what = unread === 1 ? '1 memo' : `${unread} memos`;
|
|
661
|
+
const tail = actionable > 0 ? ` (${actionable} needing action)` : '';
|
|
662
|
+
console.error(`\n✉ ${what} from the platform${tail} — read them: octwin memos`);
|
|
663
|
+
if (actionable > 0) {
|
|
664
|
+
// Said separately, because the whole point of the severity axis is that an agent
|
|
665
|
+
// mid-build should stop and read rather than finish first.
|
|
666
|
+
console.error(' One or more may change what you are building — read before continuing.');
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
catch { /* a memo check must never break the CLI */ }
|
|
670
|
+
}
|
|
623
671
|
/** Which commands already made a platform call, so the trailing KB-drift poll
|
|
624
672
|
* rides on existing network work (never on offline `validate` / `init`;
|
|
625
673
|
* `platform-kb` refreshes the reference itself, so it needs no nudge). */
|
|
@@ -632,6 +680,9 @@ function commandTouchesPlatform(command, flags) {
|
|
|
632
680
|
case 'chat':
|
|
633
681
|
case 'media':
|
|
634
682
|
case 'pull':
|
|
683
|
+
// `memos` is networked but needs NO memo nudge — it just read them. It still gets the
|
|
684
|
+
// KB drift check, which is a different question.
|
|
685
|
+
case 'memos': return true;
|
|
635
686
|
case 'records':
|
|
636
687
|
case 'work':
|
|
637
688
|
case 'logs':
|
|
@@ -2058,6 +2109,60 @@ async function cmdFeedback(flags) {
|
|
|
2058
2109
|
console.log(' ⓘ no local capability reference found, so the report carries no KB version.');
|
|
2059
2110
|
console.log(' Pull it before your next session: octwin platform-kb');
|
|
2060
2111
|
}
|
|
2112
|
+
console.log(' A reply arrives as a memo — this CLI will tell you when one is waiting.');
|
|
2113
|
+
}
|
|
2114
|
+
/**
|
|
2115
|
+
* `octwin memos [--json] [--all]` — read what the platform has told you.
|
|
2116
|
+
*
|
|
2117
|
+
* The other half of `octwin feedback`. Two kinds arrive here: a REPLY to a report you
|
|
2118
|
+
* sent, and a NOTICE we published to every author (a new capability, a deprecation, a
|
|
2119
|
+
* breaking change). Both are markdown, printed in full — this is a channel for reading,
|
|
2120
|
+
* not a list to page through, so there is no `show <id>`.
|
|
2121
|
+
*
|
|
2122
|
+
* Reading ACKS: the default prints unread memos and marks them read, so the nudge stops.
|
|
2123
|
+
* `--all` re-reads history and acks nothing, which is also the escape hatch if a piped
|
|
2124
|
+
* `--json` consumer crashed halfway through.
|
|
2125
|
+
*/
|
|
2126
|
+
async function cmdMemos(flags) {
|
|
2127
|
+
const t = resolveTarget(flags);
|
|
2128
|
+
const all = flags.all === true;
|
|
2129
|
+
const { status, json } = await apiGet(`${t.url}/api/self/p/memos${all ? '?all=1' : ''}`, t);
|
|
2130
|
+
if (status !== 200) {
|
|
2131
|
+
die(`could not read memos (HTTP ${status})${errDetail(json)}${authFailureDetail(status, t.url)}`);
|
|
2132
|
+
}
|
|
2133
|
+
const rows = (json?.rows ?? []);
|
|
2134
|
+
if (flags.json === true) {
|
|
2135
|
+
console.log(JSON.stringify(rows, null, 2));
|
|
2136
|
+
return;
|
|
2137
|
+
}
|
|
2138
|
+
if (rows.length === 0) {
|
|
2139
|
+
console.log(all ? 'No memos yet.' : 'No unread memos. (History: octwin memos --all)');
|
|
2140
|
+
return;
|
|
2141
|
+
}
|
|
2142
|
+
const mark = (s) => s === 'breaking' ? '⛔ BREAKING' : s === 'action_required' ? '⚠ ACTION' : 'ⓘ';
|
|
2143
|
+
for (const m of rows) {
|
|
2144
|
+
const when = String(m.published_at ?? '').slice(0, 10);
|
|
2145
|
+
const about = m.pack_id ? ` · ${m.pack_id}` : '';
|
|
2146
|
+
const seen = m.read_at ? ' · (already read)' : '';
|
|
2147
|
+
console.log(`\n${'─'.repeat(72)}`);
|
|
2148
|
+
console.log(`${mark(m.severity)} ${m.title}`);
|
|
2149
|
+
console.log(`${m.kind === 'reply' ? 'reply to your report' : 'platform notice'} · ${when}${about}${seen}`);
|
|
2150
|
+
console.log(`${'─'.repeat(72)}\n`);
|
|
2151
|
+
console.log(m.body_md.trimEnd());
|
|
2152
|
+
}
|
|
2153
|
+
console.log();
|
|
2154
|
+
// Ack only what we just showed as unread, and only on the default path — `--all`
|
|
2155
|
+
// deliberately leaves read state alone so history can be re-read any number of times.
|
|
2156
|
+
if (!all) {
|
|
2157
|
+
const ids = rows.filter(m => !m.read_at).map(m => m.id);
|
|
2158
|
+
if (ids.length > 0) {
|
|
2159
|
+
const res = await apiSend('POST', `${t.url}/api/self/p/memos/read`, { ids }, t);
|
|
2160
|
+
// A failed ack is not a failed command — the author HAS read them. Say so rather
|
|
2161
|
+
// than dying after printing everything, and the nudge simply fires again.
|
|
2162
|
+
if (res.status !== 200)
|
|
2163
|
+
console.error(`ⓘ could not mark them read (HTTP ${res.status}) — they will be offered again.`);
|
|
2164
|
+
}
|
|
2165
|
+
}
|
|
2061
2166
|
}
|
|
2062
2167
|
/** `octwin logs [conversationId] [--as <handle>] [--json]` — list conversations
|
|
2063
2168
|
* or show one's event timeline (full text + the renders each turn produced). */
|
|
@@ -3751,6 +3856,7 @@ function help() {
|
|
|
3751
3856
|
octwin platform-kb [pull] [--if-stale|--check] [--dir .] [--url <url>] # no token needed
|
|
3752
3857
|
octwin test [--dir .] # = validate --remote (the full platform check)
|
|
3753
3858
|
octwin feedback [--dir .] # submit this pack's FEEDBACK.md to the platform team
|
|
3859
|
+
octwin memos [--all] [--json] # read the platform's replies + notices (a reply to your feedback lands here)
|
|
3754
3860
|
|
|
3755
3861
|
Writes — exercise the state your pack creates (each needs the matching :write scope):
|
|
3756
3862
|
octwin records create <entity> --set field=value … # also: patch <id> --entity <e>, stage <id> --to <s>, note <id> "…"
|
|
@@ -3980,6 +4086,13 @@ octwin projects rm <slug> [--yes]
|
|
|
3980
4086
|
agent loops that want to branch without parsing prose.`,
|
|
3981
4087
|
test: `octwin test [--dir .]
|
|
3982
4088
|
Alias for \`octwin validate --remote\` — the full platform check.`,
|
|
4089
|
+
memos: `octwin memos [--all] [--json]
|
|
4090
|
+
Read what the platform has told you: a REPLY to a report you sent with
|
|
4091
|
+
\`octwin feedback\`, or a NOTICE published to every author (a new capability,
|
|
4092
|
+
a deprecation, a breaking change). Bodies are printed in full.
|
|
4093
|
+
Reading marks them read, so the reminder stops. --all re-reads history and
|
|
4094
|
+
acks nothing. --json to branch on \`severity\`
|
|
4095
|
+
(info | action_required | breaking).`,
|
|
3983
4096
|
feedback: `octwin feedback [--dir .]
|
|
3984
4097
|
Submit this pack's FEEDBACK.md to the platform team.
|
|
3985
4098
|
The octwin-pack skill writes that file in its last step — findings grouped by
|
|
@@ -4070,6 +4183,9 @@ async function main() {
|
|
|
4070
4183
|
case 'feedback':
|
|
4071
4184
|
await cmdFeedback(flags);
|
|
4072
4185
|
break;
|
|
4186
|
+
case 'memos':
|
|
4187
|
+
await cmdMemos(flags);
|
|
4188
|
+
break;
|
|
4073
4189
|
case 'test':
|
|
4074
4190
|
await cmdValidate({ ...flags, remote: true });
|
|
4075
4191
|
break; // A6: `test` = the full remote validate, not a validate-clone
|
|
@@ -4091,6 +4207,8 @@ async function main() {
|
|
|
4091
4207
|
// offline paths); CLI-upgrade always.
|
|
4092
4208
|
if (commandTouchesPlatform(command, flags))
|
|
4093
4209
|
await notifyIfKbStale(flags);
|
|
4210
|
+
if (commandTouchesPlatform(command, flags))
|
|
4211
|
+
await notifyIfMemosWaiting(flags);
|
|
4094
4212
|
await notifyIfOutdated();
|
|
4095
4213
|
}
|
|
4096
4214
|
main().catch((err) => die(err?.message ?? String(err)));
|
package/package.json
CHANGED