phewsh 0.15.25 → 0.15.26

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.
@@ -334,6 +334,7 @@ async function main() {
334
334
  let route = resolveRoute(config, harnesses);
335
335
  let sessionMode = null; // INTENT_MODES id once picked
336
336
  let awaitingOutcome = null; // decision id eligible for 1-4 labeling
337
+ let awaitingWhy = null; // { id, outcome } — next line is the reason
337
338
  let awaitingFallback = null; // { input, fullSystem, options } after a route failure
338
339
  let bootstrapChoices = null; // root-bootstrap menu entries when no project here
339
340
  let nextChoices = null; // ranked /next suggestions awaiting a numeric pick
@@ -597,6 +598,7 @@ async function main() {
597
598
  if (s.length > 50) s = s.slice(0, 49).trimEnd() + '…';
598
599
  const verb = hit.outcome === 'failed' ? 'failed' : 'reverted';
599
600
  console.log(` ${peach('↩')} ${sage(`You ${verb} something close before:`)} ${slate('“' + s + '” · via ' + continuity.labelFor(hit.route) + ' · ' + continuity.agoText(hit.ts))}`);
601
+ if (hit.why) console.log(` ${slate(' why it didn\'t hold: ')}${sage(hit.why)}`);
600
602
  console.log(` ${slate(' not a block — just so the record doesn\'t let you repeat it blind.')}`);
601
603
  } catch { /* recall is advisory, never blocks a turn */ }
602
604
  }
@@ -955,6 +957,16 @@ async function main() {
955
957
  async function handleInput(input) {
956
958
  input = expandPastes(input);
957
959
 
960
+ // Answering the "why?" prompt — the whole line is the reason, not a command.
961
+ if (awaitingWhy) {
962
+ const { id, outcome } = awaitingWhy;
963
+ awaitingWhy = null;
964
+ try { labelOutcome(id, outcome, input); console.log(` ${slate('noted — the record will remember why.')}`); } catch { /* gone */ }
965
+ console.log('');
966
+ rl.prompt();
967
+ return;
968
+ }
969
+
958
970
  // Any typed input supersedes a pending "did you mean" offer.
959
971
  if (pendingDidYouMean) pendingDidYouMean = null;
960
972
 
@@ -979,12 +991,22 @@ async function main() {
979
991
  // A bare 1-4 right after a routed action labels its outcome
980
992
  if (awaitingOutcome && /^[1-4]$/.test(input)) {
981
993
  const outcome = OUTCOMES[parseInt(input, 10) - 1];
994
+ let labeled = null;
982
995
  try {
983
- labelOutcome(awaitingOutcome, outcome);
996
+ labeled = labelOutcome(awaitingOutcome, outcome);
984
997
  const color = outcome === 'kept' ? green : outcome === 'superseded' ? peach : ember;
985
998
  console.log(` ${teal('●')} ${sage('outcome:')} ${color(outcome)}`);
986
999
  } catch { /* decision vanished — nothing to do */ }
1000
+ const id = awaitingOutcome;
987
1001
  awaitingOutcome = null;
1002
+ // For a regret, the reason is the gold — capture one line so /recall can
1003
+ // tell you *why* next time. Kept/superseded stay frictionless.
1004
+ if (labeled && (outcome === 'reverted' || outcome === 'failed')) {
1005
+ awaitingWhy = { id, outcome };
1006
+ console.log(` ${slate('why? one line — feeds /recall next time · enter to skip')}`);
1007
+ rl.prompt();
1008
+ return;
1009
+ }
988
1010
  console.log('');
989
1011
  rl.prompt();
990
1012
  return;
@@ -2268,6 +2290,8 @@ async function main() {
2268
2290
  const lineDispatcher = createLineDispatcher(handleInput, {
2269
2291
  onBatch: ({ input, lines }) => { collapsePastedEcho(lines, input); cmdHistory.append(input); },
2270
2292
  onNoop: () => {
2293
+ // Bare Enter skips the "why?" prompt — the label still stands.
2294
+ if (awaitingWhy) { awaitingWhy = null; console.log(''); rl.prompt(); return; }
2271
2295
  // Bare Enter accepts a pending "did you mean" suggestion.
2272
2296
  if (pendingDidYouMean) {
2273
2297
  const cmd = pendingDidYouMean;
package/lib/outcomes.js CHANGED
@@ -51,7 +51,7 @@ function recordDecision({ project, route, mode, summary }) {
51
51
  }
52
52
 
53
53
  /** Label a decision by id (or unambiguous id prefix). Returns the decision or null. */
54
- function labelOutcome(idOrPrefix, outcome) {
54
+ function labelOutcome(idOrPrefix, outcome, why = null) {
55
55
  if (!OUTCOMES.includes(outcome)) {
56
56
  throw new Error(`Outcome must be one of: ${OUTCOMES.join(', ')}`);
57
57
  }
@@ -60,6 +60,9 @@ function labelOutcome(idOrPrefix, outcome) {
60
60
  if (matches.length !== 1) return null;
61
61
  matches[0].outcome = outcome;
62
62
  matches[0].labeledAt = new Date().toISOString();
63
+ // One-line reason — the most valuable thing about a reverted/failed call.
64
+ // Feeds /recall ("you reverted this before — because …") and /learn.
65
+ if (why && String(why).trim()) matches[0].why = String(why).trim().slice(0, 200);
63
66
  save(decisions);
64
67
  return matches[0];
65
68
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "phewsh",
3
- "version": "0.15.25",
3
+ "version": "0.15.26",
4
4
  "description": "Turn intent into action. Structure your thinking, execute your next step.",
5
5
  "bin": {
6
6
  "phewsh": "bin/phewsh.js"