@tacuchi/agent-workflow-cli 21.8.0 → 21.9.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.
Files changed (60) hide show
  1. package/dist/adapters/git-cli.js +217 -0
  2. package/dist/adapters/git-cli.js.map +1 -1
  3. package/dist/application/flow/flow-service.js +7 -0
  4. package/dist/application/flow/flow-service.js.map +1 -1
  5. package/dist/application/flow/internal-actions.js +5 -0
  6. package/dist/application/flow/internal-actions.js.map +1 -1
  7. package/dist/application/history-table.js +37 -0
  8. package/dist/application/history-table.js.map +1 -1
  9. package/dist/application/local-proposal.js +37 -0
  10. package/dist/application/local-proposal.js.map +1 -1
  11. package/dist/application/retirement/apply.js +448 -0
  12. package/dist/application/retirement/apply.js.map +1 -0
  13. package/dist/application/retirement/attribution.js +395 -0
  14. package/dist/application/retirement/attribution.js.map +1 -0
  15. package/dist/application/retirement/graph.js +196 -0
  16. package/dist/application/retirement/graph.js.map +1 -0
  17. package/dist/application/retirement/history-events.js +158 -0
  18. package/dist/application/retirement/history-events.js.map +1 -0
  19. package/dist/application/retirement/journal.js +113 -0
  20. package/dist/application/retirement/journal.js.map +1 -0
  21. package/dist/application/retirement/prepare.js +233 -0
  22. package/dist/application/retirement/prepare.js.map +1 -0
  23. package/dist/application/retirement/preview.js +101 -0
  24. package/dist/application/retirement/preview.js.map +1 -0
  25. package/dist/application/retirement/resolve.js +356 -0
  26. package/dist/application/retirement/resolve.js.map +1 -0
  27. package/dist/application/session-create-service.js +103 -12
  28. package/dist/application/session-create-service.js.map +1 -1
  29. package/dist/application/session-custody-recorder.js +137 -0
  30. package/dist/application/session-custody-recorder.js.map +1 -0
  31. package/dist/application/session-custody-service.js +171 -0
  32. package/dist/application/session-custody-service.js.map +1 -0
  33. package/dist/application/status-service.js +5 -0
  34. package/dist/application/status-service.js.map +1 -1
  35. package/dist/application/workline-index-service.js +31 -0
  36. package/dist/application/workline-index-service.js.map +1 -1
  37. package/dist/application/worktree-service.js +47 -1
  38. package/dist/application/worktree-service.js.map +1 -1
  39. package/dist/cli/commands/index.js +6 -0
  40. package/dist/cli/commands/index.js.map +1 -1
  41. package/dist/cli/commands/retirement.js +137 -0
  42. package/dist/cli/commands/retirement.js.map +1 -0
  43. package/dist/cli/commands/session-create.js +4 -1
  44. package/dist/cli/commands/session-create.js.map +1 -1
  45. package/dist/cli/help-groups.js +4 -0
  46. package/dist/cli/help-groups.js.map +1 -1
  47. package/dist/domain/flow/authority.js +8 -0
  48. package/dist/domain/flow/authority.js.map +1 -1
  49. package/dist/domain/retirement/proposal.js +83 -0
  50. package/dist/domain/retirement/proposal.js.map +1 -0
  51. package/dist/domain/retirement/selector.js +109 -0
  52. package/dist/domain/retirement/selector.js.map +1 -0
  53. package/dist/domain/session/custody.js +176 -0
  54. package/dist/domain/session/custody.js.map +1 -0
  55. package/dist/domain/workline-node.js +56 -0
  56. package/dist/domain/workline-node.js.map +1 -0
  57. package/package.json +1 -1
  58. package/skills/w/commands/discard.md +49 -0
  59. package/skills/w/commands/reset.md +50 -0
  60. package/skills/w/context/MANIFEST.json +147 -139
@@ -0,0 +1,83 @@
1
+ /**
2
+ * The exact retirement somebody is about to authorize — under ONE seal.
3
+ *
4
+ * `LocalProposal` seals a publication: the bytes that will be written, the bases
5
+ * they were computed from, the effect classes. A retirement is the mirror of it
6
+ * and needs the same guarantee for a different set of facts: what DISAPPEARS,
7
+ * what goes back to bytes it used to have, which uncommitted change is dropped,
8
+ * which commit gets a revert, which unit is reconciled and which row is appended.
9
+ * So it is a record of its own, sealed the same way and over all of it.
10
+ *
11
+ * Two properties are what the seal buys, and both are properties of the data
12
+ * rather than rules somebody has to remember:
13
+ *
14
+ * - **An identical retry never re-asks.** Same closure, same bytes, same SHAs →
15
+ * same digest → the approval still fits.
16
+ * - **Anything material that moved always re-asks.** One more session in the
17
+ * closure, a plan whose bytes changed since the preview, a commit that gained a
18
+ * descendant: all of them change the digest, and the old approval stops fitting.
19
+ *
20
+ * The `read_set` is the compare-and-swap. A retirement is computed FROM a state of
21
+ * the workspace — the board, each session's custody, git — and applying it later
22
+ * is only legitimate while that state still holds. Recording what was read, with
23
+ * its digest, is what lets `apply` refuse instead of acting on a world that moved.
24
+ *
25
+ * The preview is DERIVED from this object and never authored beside it, for the
26
+ * same reason a publication's is: two descriptions of the same deletion can
27
+ * disagree, and the one a person reads would be the one that is wrong.
28
+ */
29
+ import { semanticDigest } from "../../application/semantic-operation/protocol.js";
30
+ export const RETIREMENT_MODES = ["discard", "reset"];
31
+ export const RETIREMENT_PROPOSAL_VERSION = 1;
32
+ export function sealRetirementProposal(input) {
33
+ const body = { version: RETIREMENT_PROPOSAL_VERSION, ...input };
34
+ return { ...body, digest: retirementDigest(body) };
35
+ }
36
+ /**
37
+ * The seal, over the SET rather than the order a builder happened to append in —
38
+ * except for `closure` and `reverts`, whose ORDER is material: a closure removed
39
+ * in another order can leave a dangling reference, and reverts applied in another
40
+ * order can conflict. Content travels as its own digest so the seal stays a fixed
41
+ * size whatever the restored bytes weigh.
42
+ */
43
+ export function retirementDigest(body) {
44
+ return semanticDigest({
45
+ version: body.version,
46
+ mode: body.mode,
47
+ target: body.target,
48
+ closure: body.closure,
49
+ deletes: [...body.deletes].sort((a, b) => order(a.path, b.path)),
50
+ restores: [...body.restores]
51
+ .map((r) => ({
52
+ path: r.path,
53
+ existed: r.existed,
54
+ digest: r.digest,
55
+ current_digest: r.current_digest,
56
+ }))
57
+ .sort((a, b) => order(a.path, b.path)),
58
+ bindings: [...body.bindings].sort(order),
59
+ units: [...body.units].sort((a, b) => order(`${a.alias}/${a.session}`, `${b.alias}/${b.session}`)),
60
+ dirty: [...body.dirty]
61
+ .map((d) => ({ ...d, paths: [...d.paths].sort(order) }))
62
+ .sort((a, b) => order(a.alias, b.alias)),
63
+ reverts: body.reverts,
64
+ publication: body.publication,
65
+ event: body.event,
66
+ read_set: [...body.read_set].sort((a, b) => order(a.id, b.id)),
67
+ });
68
+ }
69
+ function order(a, b) {
70
+ return a < b ? -1 : a > b ? 1 : 0;
71
+ }
72
+ /**
73
+ * Whether this proposal would touch git history at all.
74
+ *
75
+ * It is what decides whether the authorization has to be LABELLED with its
76
+ * reverts: approving a retirement that only deletes local files is not the same
77
+ * decision as approving one that adds commits to a branch, and the difference has
78
+ * to be visible in the question rather than in the preview somebody may not read.
79
+ */
80
+ export function proposesReverts(proposal) {
81
+ return proposal.reverts.length > 0;
82
+ }
83
+ //# sourceMappingURL=proposal.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"proposal.js","sourceRoot":"","sources":["../../../src/domain/retirement/proposal.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,109 @@
1
+ /**
2
+ * Naming what to retire, unambiguously or not at all.
3
+ *
4
+ * A destructive command cannot take "024" and decide what the person meant. The
5
+ * board holds a spec 024, a plan 024 and a session 024 at the same time, and each
6
+ * one is a different amount of work to delete. So the selector is EXPLICIT by
7
+ * default (`plan:024`), a bare number is accepted only while exactly one node
8
+ * answers to it, and a path is accepted because a path is already unambiguous.
9
+ *
10
+ * Parsing here is pure: it says what SHAPE was asked for. Whether that shape
11
+ * resolves to one node, several, or none is the resolver's job — the two are kept
12
+ * apart so a parse error and an ambiguous target never come back as the same
13
+ * failure.
14
+ */
15
+ import { isWorklineKind, nodeFromDocPath } from "../workline-node.js";
16
+ const BARE_NUMBER = /^\d{1,4}$/;
17
+ const SESSION_FOLDER = /^(?:session)?\d{3,}-[A-Za-z0-9._-]+$/;
18
+ export function parseTargetSelector(raw) {
19
+ const text = raw.trim();
20
+ if (text.length === 0) {
21
+ return {
22
+ ok: false,
23
+ problem: {
24
+ code: "TARGET_EMPTY",
25
+ message: "no se indicó ningún objetivo",
26
+ action: "pasá el objetivo: spec:<NNN> | plan:<PPP> | quick:<NNN> | session:<NNN|carpeta> | una ruta exacta",
27
+ },
28
+ };
29
+ }
30
+ // A path is checked first: `docs/plans/024-plan-x.md` contains no `:` on any
31
+ // platform we support, but a Windows spelling could, and the path reading is
32
+ // the more specific of the two.
33
+ const fromPath = nodeFromDocPath(text);
34
+ if (fromPath !== null) {
35
+ return {
36
+ ok: true,
37
+ selector: {
38
+ form: "path",
39
+ path: normalizeSlashes(text),
40
+ kind: fromPath.kind,
41
+ key: fromPath.key,
42
+ },
43
+ };
44
+ }
45
+ const colon = text.indexOf(":");
46
+ if (colon > 0)
47
+ return parseQualified(text, colon);
48
+ if (BARE_NUMBER.test(text))
49
+ return { ok: true, selector: { form: "bare", key: pad(text) } };
50
+ if (SESSION_FOLDER.test(text))
51
+ return { ok: true, selector: { form: "folder", folder: text } };
52
+ return {
53
+ ok: false,
54
+ problem: {
55
+ code: "TARGET_MALFORMED",
56
+ message: `'${raw}' no es un objetivo reconocible`,
57
+ action: "usá spec:<NNN>, plan:<PPP>, quick:<NNN>, session:<NNN|carpeta> o la ruta exacta del documento",
58
+ },
59
+ };
60
+ }
61
+ function parseQualified(text, colon) {
62
+ const kind = text.slice(0, colon).trim().toLowerCase();
63
+ const key = text.slice(colon + 1).trim();
64
+ if (!isWorklineKind(kind)) {
65
+ return {
66
+ ok: false,
67
+ problem: {
68
+ code: "TARGET_UNKNOWN_KIND",
69
+ message: `'${kind}' no es una clase de nodo de Workline`,
70
+ action: "las clases son spec, plan, quick y session",
71
+ },
72
+ };
73
+ }
74
+ if (key.length === 0) {
75
+ return {
76
+ ok: false,
77
+ problem: {
78
+ code: "TARGET_MALFORMED",
79
+ message: `'${kind}:' no nombra nada`,
80
+ action: `pasá ${kind}:<identidad>`,
81
+ },
82
+ };
83
+ }
84
+ // A session or a quick is addressed by number OR by folder; a spec and a plan
85
+ // only ever by number, and padding it is what makes `plan:24` and `plan:024`
86
+ // the same request instead of one that silently finds nothing.
87
+ const normalized = BARE_NUMBER.test(key) ? pad(key) : key;
88
+ return { ok: true, selector: { form: "qualified", kind, key: normalized } };
89
+ }
90
+ function pad(key) {
91
+ return key.padStart(3, "0");
92
+ }
93
+ function normalizeSlashes(path) {
94
+ return path.split("\\").join("/");
95
+ }
96
+ /** How the selector reads back to a person, verbatim in a rejection. */
97
+ export function selectorText(selector) {
98
+ switch (selector.form) {
99
+ case "qualified":
100
+ return `${selector.kind}:${selector.key}`;
101
+ case "bare":
102
+ return selector.key;
103
+ case "path":
104
+ return selector.path;
105
+ case "folder":
106
+ return selector.folder;
107
+ }
108
+ }
109
+ //# sourceMappingURL=selector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"selector.js","sourceRoot":"","sources":["../../../src/domain/retirement/selector.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,176 @@
1
+ /**
2
+ * What a session RECEIVED, what it CREATED and what it CHANGED — sealed, and
3
+ * written before the session can produce a single effect.
4
+ *
5
+ * `SESSION.md`'s `## Origin` was the only record of where a run came from, and it
6
+ * is prose: it can say "derived from the plan 024" and still not answer the two
7
+ * questions a retirement has to answer before deleting anything. Which artifacts
8
+ * existed BEFORE this run, byte for byte? And which of the things that exist now
9
+ * are this run's, as opposed to somebody else's work that happens to sit in the
10
+ * same tree? Neither is derivable from names, dates or tags, and guessing either
11
+ * one wrong destroys work nobody can reconstruct.
12
+ *
13
+ * So custody is the authority and `## Origin` becomes its human projection:
14
+ *
15
+ * - **Born with the session, in the same operation.** A session that exists
16
+ * without custody could already have mutated something by the time anybody
17
+ * thinks to record a baseline, and a baseline taken afterwards is a fiction.
18
+ * - **A baseline is bytes or an explicit absence.** "The file was not there" is a
19
+ * fact a restore needs as much as its previous content; leaving it implicit is
20
+ * what turns a reset into a delete.
21
+ * - **Effects are receipts, never inferences.** A commit's SHAs come back from
22
+ * the commit that made them. Reading ownership off a commit MESSAGE would make
23
+ * the advisor's convention load-bearing, and a message is not evidence.
24
+ * - **Incomplete is a refusal, not a partial answer.** A custody that cannot say
25
+ * what it received blocks the mutation that would promise retirement later,
26
+ * because the alternative is promising a retirement that cannot be delivered.
27
+ *
28
+ * Custody lives INSIDE its session folder and dies with it. A durable record
29
+ * outside would outlive the thing it describes — a parallel history of deleted
30
+ * work, which is precisely the surface `S025/AC-04` says must not exist.
31
+ */
32
+ import { semanticDigest } from "../../application/semantic-operation/protocol.js";
33
+ /** Bumped only when a reader can no longer trust the older shape. */
34
+ export const CUSTODY_VERSION = 1;
35
+ /** Session-local file the record lives in. Dot-prefixed: it is not an artifact. */
36
+ export const CUSTODY_FILE = ".custody.json";
37
+ /** The one way to digest a baseline's bytes, shared with the proposal's bases. */
38
+ export function baselineDigest(text) {
39
+ return semanticDigest(text);
40
+ }
41
+ /** A baseline for a path that was not there. Explicit, never an absent field. */
42
+ export const ABSENT_BASELINE = {
43
+ existed: false,
44
+ digest: null,
45
+ bytes: null,
46
+ content: null,
47
+ };
48
+ /** A baseline that preserves the previous text, for what only we can restore. */
49
+ export function preservedBaseline(content) {
50
+ return {
51
+ existed: true,
52
+ digest: baselineDigest(content),
53
+ bytes: Buffer.byteLength(content, "utf8"),
54
+ content,
55
+ };
56
+ }
57
+ export function sealCustody(input) {
58
+ const body = {
59
+ version: CUSTODY_VERSION,
60
+ subject: input.subject,
61
+ subject_path: input.subjectPath,
62
+ parents: [...(input.parents ?? [])],
63
+ created: input.created,
64
+ artifacts: [...(input.artifacts ?? [])],
65
+ sources: [...(input.sources ?? [])],
66
+ effects: [...(input.effects ?? [])],
67
+ };
68
+ return { ...body, digest: custodyDigest(body) };
69
+ }
70
+ /**
71
+ * The seal, over the SET rather than the order things were appended in.
72
+ *
73
+ * Two custodies that recorded the same facts in a different order are the same
74
+ * custody, and a digest that disagreed would report tampering on a detail nobody
75
+ * can see. Content travels as its own digest so the seal stays a fixed size
76
+ * whatever the preserved bytes weigh.
77
+ */
78
+ export function custodyDigest(body) {
79
+ return semanticDigest({
80
+ version: body.version,
81
+ subject: body.subject,
82
+ subject_path: body.subject_path,
83
+ parents: [...body.parents].sort(nodeOrder),
84
+ created: body.created,
85
+ artifacts: [...body.artifacts]
86
+ .map((a) => ({
87
+ path: a.path,
88
+ role: a.role,
89
+ before: {
90
+ existed: a.before.existed,
91
+ digest: a.before.digest,
92
+ bytes: a.before.bytes,
93
+ content_digest: a.before.content === null ? null : baselineDigest(a.before.content),
94
+ },
95
+ }))
96
+ .sort((a, b) => order(a.path, b.path)),
97
+ sources: [...body.sources].sort((a, b) => order(a.alias, b.alias)),
98
+ effects: [...body.effects],
99
+ });
100
+ }
101
+ function nodeOrder(a, b) {
102
+ return order(`${a.kind}:${a.key}`, `${b.kind}:${b.key}`);
103
+ }
104
+ /** Code-unit ordering, the same reason `canonicalJson` avoids `localeCompare`. */
105
+ function order(a, b) {
106
+ return a < b ? -1 : a > b ? 1 : 0;
107
+ }
108
+ /**
109
+ * Whether this record can still answer "what did the session receive".
110
+ *
111
+ * Called at two very different moments and that is deliberate: before a mutation
112
+ * that promises retirement, and again before a retirement is prepared. The first
113
+ * refuses to make the promise; the second refuses to pretend it was kept. Both
114
+ * read the same rule, so a session that was allowed to mutate is never one whose
115
+ * retirement turns out to be unprovable.
116
+ */
117
+ export function custodyCompleteness(custody) {
118
+ const gaps = [];
119
+ if (custody.version !== CUSTODY_VERSION) {
120
+ gaps.push({
121
+ what: `versión de custodia ${custody.version}`,
122
+ why: `este CLI sella y lee la versión ${CUSTODY_VERSION}`,
123
+ });
124
+ }
125
+ if (custody.digest !== custodyDigest(stripDigest(custody))) {
126
+ gaps.push({
127
+ what: "sello de la custodia",
128
+ why: "el registro no coincide con su digest: fue editado fuera del CLI",
129
+ });
130
+ }
131
+ for (const artifact of custody.artifacts) {
132
+ if (artifact.role !== "input")
133
+ continue;
134
+ if (artifact.before.existed && artifact.before.digest === null) {
135
+ gaps.push({
136
+ what: `baseline de '${artifact.path}'`,
137
+ why: "la entrada existía y no se conservó su estado previo",
138
+ });
139
+ }
140
+ }
141
+ for (const source of custody.sources) {
142
+ if (source.baseline_head === null && source.unit_branch !== null) {
143
+ gaps.push({
144
+ what: `baseline git de '${source.alias}'`,
145
+ why: "la sesión tomó una unidad sobre una fuente cuyo HEAD no se registró",
146
+ });
147
+ }
148
+ }
149
+ return { complete: gaps.length === 0, gaps };
150
+ }
151
+ function stripDigest(custody) {
152
+ const { digest: _digest, ...body } = custody;
153
+ return body;
154
+ }
155
+ /**
156
+ * Every commit this session can prove is its own, per source.
157
+ *
158
+ * Two independent readings, and both are receipts rather than heuristics: the
159
+ * commits a typed commit reported making, and — for a session with its own unit
160
+ * — the range its branch advanced over its recorded baseline. The second is what
161
+ * covers commits the agent made with plain git inside its unit: the branch is
162
+ * the session's by convention and the baseline is sealed, so the range is
163
+ * exclusive by construction and needs nobody's commit message.
164
+ */
165
+ export function attributableCommits(custody, alias) {
166
+ const seen = new Set();
167
+ for (const effect of custody.effects) {
168
+ if (effect.kind !== "commit" && effect.kind !== "unit_integrated")
169
+ continue;
170
+ if (effect.alias !== alias || effect.after === null)
171
+ continue;
172
+ seen.add(effect.after);
173
+ }
174
+ return [...seen];
175
+ }
176
+ //# sourceMappingURL=custody.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"custody.js","sourceRoot":"","sources":["../../../src/domain/session/custody.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,56 @@
1
+ /**
2
+ * The identity of a Workline node, said the same way by everybody who points at
3
+ * one.
4
+ *
5
+ * Provenance is a graph over four kinds of thing — a spec, a plan, a quick and a
6
+ * session — and until now each reader spelled them its own way: the index keys
7
+ * plans by `number`, sessions by folder, and a session's `## Origin` names its
8
+ * parent in prose. Prose is exactly what cannot carry an edge: two readings of
9
+ * "derived from the plan 024" can disagree about which document that is, and a
10
+ * retirement that guessed wrong would delete somebody else's work.
11
+ *
12
+ * So a node has ONE canonical id, `<kind>:<key>`, and every edge is a pair of
13
+ * those. The path is carried beside the id rather than inside it because a
14
+ * document can move: identity is `plan:024`, the path is where it lives today,
15
+ * and a stale path is a warning while a wrong id is a defect.
16
+ */
17
+ export const WORKLINE_KINDS = ["spec", "plan", "quick", "session"];
18
+ const KIND_SET = new Set(WORKLINE_KINDS);
19
+ export function isWorklineKind(value) {
20
+ return typeof value === "string" && KIND_SET.has(value);
21
+ }
22
+ /** `plan:024` — the one spelling of a node, in output and in a seal alike. */
23
+ export function formatNodeId(id) {
24
+ return `${id.kind}:${id.key}`;
25
+ }
26
+ const DOC_PATH_RE = /(?:^|\/)docs\/(specs|plans)\/(\d{3})-(spec|plan)(?:-[^/]*)?\.md$/i;
27
+ /**
28
+ * The node a document path IS — `docs/plans/024-plan-x.md` is `plan:024`.
29
+ *
30
+ * This is not inferring provenance from a name: the workspace's own layout fixes
31
+ * that a spec lives at `docs/specs/NNN-spec-*.md`, and the board already reads
32
+ * every spec and plan by exactly this shape. Identity is what the path declares;
33
+ * what the document is DERIVED from is a different question, answered by the
34
+ * document's own `Derived from` line or by a session's sealed custody.
35
+ */
36
+ export function nodeFromDocPath(path) {
37
+ const m = DOC_PATH_RE.exec(path.split("\\").join("/"));
38
+ if (m?.[2] === undefined)
39
+ return null;
40
+ return { kind: m[1]?.toLowerCase() === "specs" ? "spec" : "plan", key: m[2] };
41
+ }
42
+ /**
43
+ * Why an edge is believed.
44
+ *
45
+ * `custody` and `derived-from` are provable: one was sealed by the CLI when the
46
+ * node was born, the other is the plan's own machine-readable `Derived from`
47
+ * line. `origin-prose` is the legacy reading of a free-text `## Origin`, and it
48
+ * is kept SEPARATE precisely so a retirement can refuse to act on it — an edge
49
+ * nobody can prove is the one case where deleting is not recoverable.
50
+ */
51
+ export const EDGE_EVIDENCE = ["custody", "derived-from", "origin-prose"];
52
+ /** Whether an edge is strong enough for a destructive closure to include it. */
53
+ export function isProvable(evidence) {
54
+ return evidence === "custody" || evidence === "derived-from";
55
+ }
56
+ //# sourceMappingURL=workline-node.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workline-node.js","sourceRoot":"","sources":["../../src/domain/workline-node.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,CAAU,CAAC;AAa5E,MAAM,QAAQ,GAAwB,IAAI,GAAG,CAAC,cAAc,CAAC,CAAC;AAE9D,MAAM,UAAU,cAAc,CAAC,KAAc;IAC3C,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;AAC1D,CAAC;AAED,8EAA8E;AAC9E,MAAM,UAAU,YAAY,CAAC,EAAkB;IAC7C,OAAO,GAAG,EAAE,CAAC,IAAI,IAAI,EAAE,CAAC,GAAG,EAAE,CAAC;AAChC,CAAC;AAED,MAAM,WAAW,GAAG,mEAAmE,CAAC;AAExF;;;;;;;;GAQG;AACH,MAAM,UAAU,eAAe,CAAC,IAAY;IAC1C,MAAM,CAAC,GAAG,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IACvD,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,SAAS;QAAE,OAAO,IAAI,CAAC;IACtC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,KAAK,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;AAChF,CAAC;AAiBD;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,SAAS,EAAE,cAAc,EAAE,cAAc,CAAU,CAAC;AAIlF,gFAAgF;AAChF,MAAM,UAAU,UAAU,CAAC,QAAsB;IAC/C,OAAO,QAAQ,KAAK,SAAS,IAAI,QAAQ,KAAK,cAAc,CAAC;AAC/D,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tacuchi/agent-workflow-cli",
3
- "version": "21.8.0",
3
+ "version": "21.9.0",
4
4
  "description": "Runtime CLI for Workline — the stages + loops + artifacts system for agent work. Bundles the universal `w` skill set under `skills/w/` (slash commands `/w:*`: spec-new/spec-refine, plan-new/plan-exec, quick, persist, workspace-init, export-*); `self install --target <host>` copies SKILL + commands + hooks into the host. Pluggable capability skills via `.workflow/skills.toml`. Multi-empresa parametrization via `profile.json` cascade. Namespace auto-detected from any `.<ns>/sessions/` dir in CWD; default `workflow`.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -0,0 +1,49 @@
1
+ ---
2
+ description: Use to retire a spec, plan, quick or session for good with everything it exclusively owns. `aw discard` resolves the closure from sealed provenance, previews it whole and applies it all-or-nothing on one approval. Never rewrites git history.
3
+ argument-hint: "<spec:NNN | plan:PPP | quick:NNN | session:NNN|carpeta | ruta>"
4
+ allowed-tools: ["Bash", "Read"]
5
+ ---
6
+
7
+ # discard — retire work and everything it exclusively owns
8
+
9
+ No loop, no session — the closure can include the session that would be driving it.
10
+ Output in the **user's language**.
11
+
12
+ **Hard floor:**
13
+
14
+ 1. **`prepare` first, always** — read-only: no session, no journal, no file, no ref,
15
+ so a wrong target costs nothing.
16
+ 2. **You never decide the scope.** It comes from sealed custody and each document's
17
+ `Derived from`; a descendant nobody can prove is a REFUSAL, not a guess.
18
+ 3. **One approval, over the exact digest.** `apply` recomputes under the workspace
19
+ lock and refuses if anything material moved. Approving is not applying.
20
+ 4. **Reverts are commits, never rewrites** — no `reset --hard`, rebase, amend, force
21
+ or push; a published commit's revert leaves its push pending and external.
22
+
23
+ ## Run
24
+
25
+ 1. `aw discard prepare <objetivo> --format human` — what disappears, which local
26
+ change is dropped, which SHA gets a revert, which unit is reconciled, which
27
+ `HISTORY` row it adds, and the **digest**.
28
+ 2. Show that preview and ask approval. If it touches git history, say so: approving
29
+ also authorizes commits.
30
+ 3. `aw discard apply <objetivo> --approval <digest> --format human`.
31
+ 4. A rejection names cause, candidates and next action; **nothing was applied**.
32
+ `024` resolves only while one node answers to it.
33
+
34
+ ## What the CLI decides (do not re-derive)
35
+
36
+ - **The closure**, in removal order; a node something outside also descends from
37
+ blocks the whole operation.
38
+ - **Attribution** of changes and commits, from baselines and receipts — never from a
39
+ message, an author or a tag.
40
+ - **Whether all-or-nothing holds**: two publication units, a conflicting revert, an
41
+ operation in progress or an unsyncable tree block before anything mutates.
42
+ - **What survives**: the originals stay reachable and the only durable Workline trace
43
+ is one append-only `HISTORY` row.
44
+
45
+ ## More context
46
+
47
+ `aw context-plan --command discard --root "${CLAUDE_PLUGIN_ROOT}/skills/w"` lists what to read.
48
+
49
+ - Back to before an **incomplete** session instead: `/w:reset`.
@@ -0,0 +1,50 @@
1
+ ---
2
+ description: Use to undo an incomplete session and put its document back as it was before that session ran. `aw reset` restores the inputs byte for byte and retires the session, all-or-nothing on one approval. Never rewrites git history.
3
+ argument-hint: "<plan:PPP | session:NNN|carpeta | ruta>"
4
+ allowed-tools: ["Bash", "Read"]
5
+ ---
6
+
7
+ # reset — put an incomplete session's inputs back
8
+
9
+ No loop, no session. Output in the **user's language**. It is **not**
10
+ `/w:plan-refine`: refining carries a document forward keeping validated phases, this
11
+ takes it back to the bytes it had before the session started.
12
+
13
+ **Hard floor:**
14
+
15
+ 1. **`prepare` first, always** — read-only, so a wrong target costs nothing.
16
+ 2. **Only an INCOMPLETE session.** One that converged does not become resettable by
17
+ being selected, and `.closed` is never the test: a session closed with work
18
+ pending is exactly this case.
19
+ 3. **The target must resolve to ONE incomplete session**; two is a rejection with its
20
+ candidates, and then you name the session.
21
+ 4. **One approval, over the exact digest.** `apply` recomputes under the lock and
22
+ refuses if the document moved since the preview.
23
+ 5. **Reverts are commits, never rewrites** — no `reset --hard`, rebase, amend, force
24
+ or push, whatever this command's name suggests.
25
+
26
+ ## Run
27
+
28
+ 1. `aw reset prepare <objetivo> --format human` — which session it resolved, which
29
+ paths go back to their previous bytes and whether they moved since the baseline,
30
+ what disappears, which SHA gets a revert, and the **digest**.
31
+ 2. Show that preview and ask approval; if it touches git history, say so.
32
+ 3. `aw reset apply <objetivo> --approval <digest> --format human`.
33
+ 4. A rejection names cause, candidates and next action; **nothing was applied**.
34
+
35
+ ## What the CLI decides (do not re-derive)
36
+
37
+ - **Which session** an artifact resolves to, and whether it is incomplete.
38
+ - **What "before" was**: the baseline sealed at creation. Never reconstructed from
39
+ names, dates or tags — without it, it fails closed.
40
+ - **Inputs vs outputs**: an input returns to its bytes, an output is removed, and a
41
+ document with work of its own blocks instead of being orphaned. A quick with no
42
+ base document is retired whole.
43
+ - **All-or-nothing**: either the scope is intact, or the result is complete, not
44
+ resumable, and leaves one append-only `HISTORY` row.
45
+
46
+ ## More context
47
+
48
+ `aw context-plan --command reset --root "${CLAUDE_PLUGIN_ROOT}/skills/w"` lists what to read.
49
+
50
+ - Deleting a document and everything under it: `/w:discard`.