alissa-tools-github-devloop 0.8.44__py3-none-any.whl → 0.8.46__py3-none-any.whl

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.
@@ -1964,6 +1964,16 @@ def write_discovery_marker(session: str, plans_repo: str, number: int, discovery
1964
1964
  # `escalation.first-run-dialog`, anchored on the issue.
1965
1965
  ESCALATION_FIRST_RUN_DIALOG = "first-run-dialog"
1966
1966
 
1967
+ # The claude-exited wedge (issue #169): a session the roster lists ALIVE
1968
+ # whose pane is back at its login shell -- `claude` exited, the tmux session
1969
+ # stayed. WEDGE_CLAUDE_EXITED is the classification the loop logs and puts
1970
+ # on the activity line; ESCALATION_CLAUDE_EXITED is its once-per-episode
1971
+ # marker (see claude_exited_kind), an ACT like ESCALATION_FIRST_RUN_DIALOG:
1972
+ # it dedupes the WARNING and the activity line, and the loop-events emitter
1973
+ # turns it into `escalation.claude_exited`, anchored on the issue.
1974
+ WEDGE_CLAUDE_EXITED = "wedged:claude-exited"
1975
+ ESCALATION_CLAUDE_EXITED = "claude-exited"
1976
+
1967
1977
  # The maintenance edge's cap-out kind prefix. The `maintain-` prefix keeps
1968
1978
  # rows from ever colliding with the issue edge's `cap` when an issue and a
1969
1979
  # PR share a number; the epoch suffix is maintain_cap_kind's job below.
@@ -2353,6 +2363,15 @@ def first_run_dialog_kind(attempt: int) -> str:
2353
2363
  return f"{ESCALATION_FIRST_RUN_DIALOG}:a{attempt}"
2354
2364
 
2355
2365
 
2366
+ def claude_exited_kind(attempt: int) -> str:
2367
+ """The ledger kind for ONE claude-exited wedge episode (issue #169).
2368
+
2369
+ first_run_dialog_kind's episode reasoning: every attempt's claude can
2370
+ exit, and attempt k's row must not silence attempt k+1's WARNING.
2371
+ """
2372
+ return f"{ESCALATION_CLAUDE_EXITED}:a{attempt}"
2373
+
2374
+
2356
2375
  def deferral_activity_kind(session: str) -> str:
2357
2376
  """The activity-log kind that dedupes ONE capacity-deferral episode's line.
2358
2377
 
@@ -4016,7 +4035,10 @@ class DevWatcher:
4016
4035
  # terminal one.
4017
4036
  resume_pr: "PullRequest | None" = None
4018
4037
  dead_session: "str | None" = None
4019
- wedged_dialog = False
4038
+ # A wedge kill that clears the lane THIS pass (the first-run dialog,
4039
+ # a claude that exited) logged its own WARNING: the re-enqueue's
4040
+ # "absent from the tmux listing" line below would misstate it.
4041
+ wedge_killed = False
4020
4042
  if age is not None:
4021
4043
  # `exhaustive` only when a resume could actually fire: the probe's
4022
4044
  # default early break answers the boolean the deferral needs, but
@@ -4043,7 +4065,25 @@ class DevWatcher:
4043
4065
  issue.full_name, number
4044
4066
  )
4045
4067
  liveness = self.session_liveness(row_session, roster)
4046
- if self.config.resume_enabled and liveness is Liveness.DEAD:
4068
+ if liveness is Liveness.ALIVE and row_session:
4069
+ killed, exited_resume = self._claude_exited(
4070
+ issue, attempts, row_session, linked
4071
+ )
4072
+ if killed:
4073
+ # Issue #169: listed, but its claude is gone -- the
4074
+ # kill landed, so the lane is DEAD from here on, and
4075
+ # the `_resumable_pr` answer the kill's outcome line
4076
+ # was written from is the one this pass acts on: a
4077
+ # still-draft PR is resumed below, anything else
4078
+ # keeps the (now dead) defer.
4079
+ wedge_killed = True
4080
+ liveness = Liveness.DEAD
4081
+ resume_pr = exited_resume
4082
+ if (
4083
+ self.config.resume_enabled
4084
+ and liveness is Liveness.DEAD
4085
+ and not wedge_killed
4086
+ ):
4047
4087
  resume_pr = self._resumable_pr(issue, linked)
4048
4088
  elif (
4049
4089
  self.config.resume_enabled
@@ -4165,7 +4205,19 @@ class DevWatcher:
4165
4205
  # successful listing without this name) -- so the retry below
4166
4206
  # can name the session it proved gone instead of presuming.
4167
4207
  dead_session = live_session
4168
- if liveness is Liveness.ALIVE:
4208
+ if (
4209
+ liveness is Liveness.ALIVE
4210
+ and self._claude_exited(
4211
+ issue, attempts, live_session, None
4212
+ )[0]
4213
+ ):
4214
+ # Issue #169: ALIVE is a tmux session, not a claude. The
4215
+ # pane is back at its login shell, the kill landed: fall
4216
+ # through to the cap check and the re-enqueue below as
4217
+ # the CONFIRMED-dead path would.
4218
+ wedge_killed = True
4219
+ liveness = Liveness.DEAD
4220
+ elif liveness is Liveness.ALIVE:
4169
4221
  # Issue #123: ALIVE is a process, not a worker. A session
4170
4222
  # sitting on Claude Code's first-run "trust this folder?"
4171
4223
  # (or bypass-permissions) gate is listed, idle, and will
@@ -4177,7 +4229,7 @@ class DevWatcher:
4177
4229
  if self._wedged_on_first_run_dialog(
4178
4230
  issue, attempts, live_session
4179
4231
  ):
4180
- wedged_dialog = True
4232
+ wedge_killed = True
4181
4233
  # The kill landed and the hub is seeded: fall through
4182
4234
  # to the cap check and the re-enqueue below as the
4183
4235
  # CONFIRMED-dead path would -- one WARNING for the
@@ -4270,7 +4322,7 @@ class DevWatcher:
4270
4322
  attempts,
4271
4323
  )
4272
4324
 
4273
- if age is not None and resume_pr is not None:
4325
+ if age is not None and resume_pr is not None and not wedge_killed:
4274
4326
  log.warning(
4275
4327
  "%s attempt %d has been in flight %.0f min behind its still-"
4276
4328
  "draft open PR %s and its session %s is absent from the tmux "
@@ -4283,7 +4335,7 @@ class DevWatcher:
4283
4335
  dead_session,
4284
4336
  attempts + 1,
4285
4337
  )
4286
- elif age is not None and not wedged_dialog:
4338
+ elif age is not None and not wedge_killed:
4287
4339
  log.warning(
4288
4340
  "%s attempt %d has been in flight %.0f min with no completion "
4289
4341
  "and no linked open PR, and its session %s is absent from the "
@@ -5083,6 +5135,157 @@ class DevWatcher:
5083
5135
  return None
5084
5136
  return min(youngest, max(0.0, now - committed))
5085
5137
 
5138
+ def _claude_exited(
5139
+ self,
5140
+ issue: Issue,
5141
+ attempts: int,
5142
+ session: str,
5143
+ linked: "list[str] | None",
5144
+ ) -> "tuple[bool, PullRequest | None]":
5145
+ """Issue #169: is this stale, ALIVE session's `claude` gone -- and
5146
+ if so, clear the lane.
5147
+
5148
+ ALIVE means the roster LISTS the session; it says nothing about the
5149
+ process the session was spawned for. A claude that exits (a crash,
5150
+ a `/exit`, a kill from inside) leaves the tmux session open with its
5151
+ pane back at the login shell, and the session read alive for as long
5152
+ as anyone cared to look -- 2026-10-13, four worker sessions for
5153
+ 1.5-2 h, behind the "work may be alive" and "may still be running"
5154
+ defers, until an operator killed them by hand. The probe is the one
5155
+ `display-message` `_first_turn_death` already uses: a pane whose
5156
+ `#{pane_current_command}` is one of `SHELL_COMMANDS` has no claude
5157
+ (nor the `alissa code` wrapper) in the foreground. A pane that
5158
+ reads anything else -- `node`, the wrapper -- is a live worker and
5159
+ keeps every defer exactly as before; "" (tmux could not answer) is
5160
+ absence of evidence, never a dead claude, and keeps them too. A
5161
+ usage-limited session is never killed (issue #144).
5162
+
5163
+ A match is `wedged:claude-exited`: ONE WARNING per episode (keyed
5164
+ claude_exited_kind(attempts); a failed kill retried next poll logs
5165
+ at INFO), then the row's own session is killed (`alissa tmux kill
5166
+ <name>`, the path every wedge kill takes, never a sweep), one
5167
+ activity-comment line records the act, and a True first element
5168
+ tells the caller to treat the session as CONFIRMED dead NOW. The
5169
+ pre-PR branch (`linked` None) re-enqueues attempt k+1. The linked-PR
5170
+ branch passes its open PRs, and `_resumable_pr` is asked HERE,
5171
+ before a word is logged, because only its answer says what the lane
5172
+ does next: a still-draft PR is resumed as attempt k+1 (the PR is the
5173
+ second element, which the caller acts on rather than asking again),
5174
+ while a handed-off PR, several candidates or `resume_enabled: false`
5175
+ leave the lane deferring, now read dead -- and the WARNING and the
5176
+ activity line say THAT, never a resume the lane will not get. A
5177
+ re-enqueue or a resume counts toward attempt_cap, and on the
5178
+ terminal attempt the caller's cap check pages the operator instead,
5179
+ which the lines say rather than promising a retry (a deferring lane
5180
+ never reaches that check, so it is never told it caps out).
5181
+ Dry-run classifies and logs but kills nothing and answers False
5182
+ (the defer holds); so does a kill that fails, retried next poll.
5183
+
5184
+ Not done here: the process groups the session's claude started
5185
+ that outlived it (a test run re-parented to init). Nothing on the
5186
+ daemon's side ties such a group to a session once its parent is
5187
+ gone, so they are left to the operator (see the README)."""
5188
+ command = self.alissa.pane_command(session)
5189
+ if command not in SHELL_COMMANDS:
5190
+ return False, None
5191
+ if self.state.session_limited(session):
5192
+ return False, None
5193
+ kind = claude_exited_kind(attempts)
5194
+ resume_pr = (
5195
+ self._resumable_pr(issue, linked)
5196
+ if linked is not None and self.config.resume_enabled
5197
+ else None
5198
+ )
5199
+ deferring = linked is not None and resume_pr is None
5200
+ capping = not deferring and attempts >= self.config.attempt_cap
5201
+ if deferring:
5202
+ outcome = (
5203
+ "leaving the lane to the review lifecycle: no linked PR is "
5204
+ "resumable (a handed-off PR is not resumed), so it keeps its "
5205
+ "defer, now reading dead"
5206
+ )
5207
+ activity_outcome = (
5208
+ "no linked PR is resumable, so the lane waits on the review "
5209
+ "lifecycle"
5210
+ )
5211
+ elif capping:
5212
+ outcome = (
5213
+ f"NOT re-enqueuing: attempt {attempts} was the last "
5214
+ f"(attempt_cap {self.config.attempt_cap}), so the lane caps "
5215
+ f"out and pages the operator"
5216
+ )
5217
+ activity_outcome = (
5218
+ f"attempt_cap {self.config.attempt_cap} reached — the lane "
5219
+ f"caps out and pages the operator instead of re-enqueuing"
5220
+ )
5221
+ elif resume_pr is not None:
5222
+ outcome = (
5223
+ f"resuming its still-draft PR {resume_pr.url} as attempt "
5224
+ f"{attempts + 1} (counting toward attempt_cap "
5225
+ f"{self.config.attempt_cap})"
5226
+ )
5227
+ activity_outcome = (
5228
+ f"its still-draft PR is resumed as attempt {attempts + 1}"
5229
+ )
5230
+ else:
5231
+ outcome = (
5232
+ f"re-enqueuing as attempt {attempts + 1} (counting toward "
5233
+ f"attempt_cap {self.config.attempt_cap})"
5234
+ )
5235
+ activity_outcome = f"re-enqueued as attempt {attempts + 1}"
5236
+ if self.state.escalated(issue.full_name, issue.number, kind):
5237
+ log.info(
5238
+ "%s: session %s (%s attempt %d) still has no claude in its "
5239
+ "pane — retrying the kill",
5240
+ WEDGE_CLAUDE_EXITED, session, issue.issue_slug, attempts,
5241
+ )
5242
+ else:
5243
+ log.warning(
5244
+ "%s: session %s (%s attempt %d) reads alive but its pane is "
5245
+ "back at the shell (%s) — its claude exited and nothing will "
5246
+ "ever be published. Killing it and %s",
5247
+ WEDGE_CLAUDE_EXITED, session, issue.issue_slug, attempts,
5248
+ command, outcome,
5249
+ )
5250
+ self.state.record_escalation(issue.full_name, issue.number, kind)
5251
+ if self.config.dry_run:
5252
+ log.info(
5253
+ "[dry-run] would kill %s (claude exited) and %s",
5254
+ session,
5255
+ "keep the defer" if deferring
5256
+ else f"cap out (attempt_cap {self.config.attempt_cap})"
5257
+ if capping
5258
+ else f"resume attempt {attempts + 1}" if resume_pr is not None
5259
+ else f"re-enqueue attempt {attempts + 1}",
5260
+ )
5261
+ return False, None
5262
+ try:
5263
+ self.alissa.kill_session(session)
5264
+ except CommandError as exc:
5265
+ log.warning(
5266
+ "%s: could not kill %s (%s) — kills are best-effort; the lane "
5267
+ "keeps its defer and the kill is retried next poll",
5268
+ WEDGE_CLAUDE_EXITED, session, exc,
5269
+ )
5270
+ return False, None
5271
+ if not self.state.activity_logged(issue.full_name, issue.number, kind):
5272
+ landed = self._append_activity(
5273
+ issue.owner,
5274
+ issue.repo,
5275
+ issue.number,
5276
+ self._activity_line(
5277
+ session,
5278
+ "issue edge",
5279
+ f"{ACTIVITY_WEDGE_KILLED} — {WEDGE_CLAUDE_EXITED}: the "
5280
+ f"session read alive but its pane was back at the shell "
5281
+ f"(claude exited); killed, {activity_outcome}",
5282
+ ),
5283
+ issue.issue_slug,
5284
+ )
5285
+ if landed:
5286
+ self.state.record_activity(issue.full_name, issue.number, kind)
5287
+ return True, resume_pr
5288
+
5086
5289
  def _wedged_on_first_run_dialog(
5087
5290
  self, issue: Issue, attempts: int, session: str
5088
5291
  ) -> bool:
@@ -187,6 +187,7 @@ _LIMIT_LIFTED_RE = re.compile(r"^usage-limit-lifted:(?P<session>[^@]+)@(?P<since
187
187
  # points the operator at an unrelated PR or at nothing.
188
188
  _ISSUE_ANCHORED = (
189
189
  "cap", "assignment", "stalled", "live-stalled", "wedged", "first-run-dialog",
190
+ "claude-exited",
190
191
  )
191
192
 
192
193
 
@@ -25,7 +25,7 @@ serves it on ``GET /api/state`` and runs the drain handshake):
25
25
  null as a refusal.
26
26
  * **The drain.** A flag with a TTL in `state.db` (`State.set_drain`), cleared
27
27
  when the daemon boots. While it holds, every spawn path stands down before
28
- its first side effect (`DevWatcher._drain_gate`).
28
+ its first side effect (`DevWatcher._drain_gated`).
29
29
  """
30
30
 
31
31
  from __future__ import annotations
@@ -1 +1 @@
1
- 0.8.44
1
+ 0.8.46
@@ -234,7 +234,16 @@ class App:
234
234
  down and the answer is `drained: false` with the reasons. When the
235
235
  drain holds, a vitals snapshot carrying `idle.drainedAt` is pushed
236
236
  BEFORE the answer, because that snapshot is what Studio re-checks
237
- before it removes anything; the answer says how the push went."""
237
+ before it removes anything; the answer says how the push went.
238
+
239
+ A refused drain lowers the flag, not just its own raise: the clear is
240
+ unscoped (`State.clear_drain`). A refused RE-drain therefore also
241
+ lowers an earlier drain that held and had answered `drained: true`,
242
+ and so does a second, overlapping `/action/drain` that is refused.
243
+ That is intended (seat-parking §2.4: when the block breaks D2 "the
244
+ seat clears the flag itself and answers `drained: false`"): a seat
245
+ that is no longer idle must not stay drained, whoever raised the
246
+ flag."""
238
247
  if ttl_s is None:
239
248
  ttl = parking.DRAIN_TTL_DEFAULT
240
249
  elif (
@@ -265,7 +274,7 @@ class App:
265
274
  except sqlite3.Error as exc:
266
275
  self._audit("drain", {"ok": False, "error": str(exc)})
267
276
  return 503, {"drained": False, "error": "state_unavailable"}
268
- listing = self.sources._safe_json(["alissa", "tmux", "ls", "--json"])
277
+ listing = self.sources.listing()
269
278
  idle = self.sources.idle(listing=listing)
270
279
  refusals = parking.idle_refusals(
271
280
  idle, poll_interval=self.sources.config.poll_interval, now=now,
@@ -305,13 +314,18 @@ class App:
305
314
  def undrain(self) -> "tuple[int, dict]":
306
315
  """`POST /action/undrain`: lower the drain flag -- orcloop's step 4
307
316
  on any failure of a park. Idempotent: undraining a seat that is not
308
- drained answers `cleared: false`. (status, payload), audited."""
317
+ drained answers `cleared: false`. (status, payload), audited.
318
+
319
+ A ledger that cannot be written answers 503 with `drained: null`:
320
+ nobody knows then whether the flag is up (there may be no drain at
321
+ all), and unknown is never a value (design D2). The 503 already says
322
+ the undrain did not land; the TTL still bounds any flag left up."""
309
323
  try:
310
324
  with State(self.sources.config.state_db) as st:
311
325
  cleared = st.clear_drain()
312
326
  except sqlite3.Error as exc:
313
327
  self._audit("undrain", {"ok": False, "error": str(exc)})
314
- return 503, {"drained": True, "error": "state_unavailable"}
328
+ return 503, {"drained": None, "error": "state_unavailable"}
315
329
  self._audit("undrain", {"ok": True, "cleared": cleared})
316
330
  return 200, {"drained": False, "cleared": cleared}
317
331
 
@@ -384,6 +384,15 @@ class Sources:
384
384
  except ValueError:
385
385
  return None
386
386
 
387
+ def listing(self) -> "object":
388
+ """The parsed ``alissa tmux ls --json`` listing, or None when it could
389
+ not be read (the command failed, printed nothing, or printed something
390
+ that is not JSON). The console's one reader of the tmux roster: the
391
+ sessions panel, the idle block, the dashboard and the drain handshake
392
+ all read it here. The value is whatever the CLI printed -- callers
393
+ check it is a list before trusting its shape."""
394
+ return self._safe_json(["alissa", "tmux", "ls", "--json"])
395
+
387
396
  def sessions(self, listing: "list[dict] | None" = None) -> "list[dict]":
388
397
  """The managed sessions with their edge, age and `/proc` footprint.
389
398
 
@@ -393,9 +402,7 @@ class Sources:
393
402
  console (no listing) lists for itself. Either way the rows go
394
403
  through the same shaping, so the two never disagree about what a
395
404
  session record looks like."""
396
- raw = listing if listing is not None else (
397
- self._safe_json(["alissa", "tmux", "ls", "--json"]) or []
398
- )
405
+ raw = listing if listing is not None else (self.listing() or [])
399
406
  if not isinstance(raw, list):
400
407
  return []
401
408
  now = int(self._wall())
@@ -463,7 +470,7 @@ class Sources:
463
470
  snaps = self.snapshots(1)
464
471
  latest = snaps[0] if snaps else None
465
472
  if listing is _UNREAD:
466
- listing = self._safe_json(["alissa", "tmux", "ls", "--json"])
473
+ listing = self.listing()
467
474
  roster = listing if isinstance(listing, list) else None
468
475
  snapshot = latest if isinstance(latest, dict) else None
469
476
  queues = self._safe_json(["alissa", "tmux", "queue", "ls", "--json"])
@@ -678,7 +685,7 @@ class Sources:
678
685
  latest = snaps[0] if snaps else None
679
686
  ledgers = self.ledgers()
680
687
  # One listing feeds both the sessions panel and the idle block.
681
- listing = self._safe_json(["alissa", "tmux", "ls", "--json"])
688
+ listing = self.listing()
682
689
  sessions = self.sessions(
683
690
  listing=listing if isinstance(listing, list) else []
684
691
  )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: alissa-tools-github-devloop
3
- Version: 0.8.44
3
+ Version: 0.8.46
4
4
  Summary: ALISSA-TOOLS-GITHUB-DEVLOOP
5
5
  Home-page: https://alissa.app
6
6
  Author: Fahera
@@ -220,6 +220,32 @@ kills or reaps one. The fleet-vitals push applies the same rule to
220
220
  `sessions.managed` and `idle.managed` (Studio's park guard reads both) and
221
221
  leaves `gone` out, since Studio's idle schema is strict.
222
222
 
223
+ ## Wedges: alive is not working
224
+
225
+ A stale issue-edge session the tmux roster lists ALIVE is not trusted on
226
+ that alone. The daemon kills the row's own session (`alissa tmux kill
227
+ <name>`, never a sweep) and moves the lane on when the session is wedged:
228
+
229
+ - **`wedged:claude-exited`** (issue #169) — the pane's
230
+ `#{pane_current_command}` is a login shell (`alissa.SHELL_COMMANDS`): the
231
+ `claude` the session was spawned for exited and the tmux session stayed.
232
+ Checked on both stale branches (with and without a linked open PR), no
233
+ `wedge_minutes` needed; the session reads DEAD in the same pass, so the
234
+ pre-PR lane re-enqueues attempt k+1 and a draft-PR lane resumes its PR as
235
+ attempt k+1, counting toward `attempt_cap`. Ledger kind
236
+ `claude-exited:a<k>`.
237
+ - **`wedged:first-run-dialog`** (issue #123) — the pane sits on Claude
238
+ Code's trust / bypass-permissions gate: killed, hub pre-trusted,
239
+ re-enqueued in the same pass.
240
+ - **footprint wedges** (issues #110, #127; opt-in `wedge_minutes`) — no
241
+ activity footprint inside the window: killed, and the next pass resumes
242
+ or retries.
243
+
244
+ A pane tmux cannot read keeps the defer, a usage-limited session is never
245
+ killed (issue #144), and dry-run only logs. The root README's *Claude
246
+ exited, the session stayed* section has the details. Processes a dead claude started that
247
+ outlived it (re-parented to init) are not reaped — a known follow-up.
248
+
223
249
  ## License
224
250
 
225
251
  Licensed under the
@@ -8,25 +8,25 @@ alissa/tools/github/devloop/fleet_vitals.py,sha256=5mWnRkJh8u9soSplud3UMxkelT6Eq
8
8
  alissa/tools/github/devloop/ghclient.py,sha256=tYUHwmoy9CBBt-jSOz9nuAl8lpMvm1LH4A-wHx25bss,71842
9
9
  alissa/tools/github/devloop/hubs.py,sha256=yjnsnSHLG7GNNP8X2hILeAMV6P5n4JGc_IaAxpSP_YQ,18974
10
10
  alissa/tools/github/devloop/janitor.py,sha256=R8REoHBeY2cfW7fYJVhKr-F5z9Mjlc-vmv-z4KtvBZc,27360
11
- alissa/tools/github/devloop/loop.py,sha256=uMfuomSdIPC8KkWUGF0OrW3RR9OSC1txKVnXGZ1SeIc,613973
12
- alissa/tools/github/devloop/loop_events.py,sha256=tmso0OALqY2yAPW2Fo6ae0sYetQIdIeIYooJ2Sc2cc4,37921
13
- alissa/tools/github/devloop/parking.py,sha256=vEf-qprN14-I4vu4M53tIFaA3827FiHLpKetBAgN4LQ,12409
11
+ alissa/tools/github/devloop/loop.py,sha256=I5rZy082JqplmFm1t2XgYkstc7IdkJJdX-FXAfwqcKk,624114
12
+ alissa/tools/github/devloop/loop_events.py,sha256=pKtBl1HK8gYPB2gKiI29xUkx-JuW2A48KwRlbdcf_lo,37942
13
+ alissa/tools/github/devloop/parking.py,sha256=7h3Gg2XQ0eqlvn0M6K6NeEg9xbqnlUPrZNs9gRofzxc,12410
14
14
  alissa/tools/github/devloop/proc.py,sha256=nzfsxJSHFWRQcrXmfv588KazFysv8dVQrhMPH1q5eRk,3134
15
15
  alissa/tools/github/devloop/prompts.py,sha256=VzN6ec5eF2wwg7QSCRg52WLcxo-tGbGRgQbc4HSahUc,55169
16
16
  alissa/tools/github/devloop/state.py,sha256=uVwhbkogUTjWXXFq-COKzUeYrpuGbHzi05CjfEzAtPA,88668
17
- alissa/tools/github/devloop/version,sha256=va232A0BU1OdMrEBtZ5PyqpxusfsBdgt4whBySGjLHY,7
17
+ alissa/tools/github/devloop/version,sha256=YpzpT7756Lld10FKXeESQnDWJA9HvJScK9ngOSfSk8s,7
18
18
  alissa/tools/github/devloop/version.py,sha256=znuuOaitXGrBpbskNy78_rlVpFdtmKmk_3k1ACxZrl0,1418
19
19
  alissa/tools/github/devloop/webui/__init__.py,sha256=vdXYoyGYzD7BrJ5lV6XdH8Bpw_pej8-RK0hjfxop7-8,1513
20
20
  alissa/tools/github/devloop/webui/__main__.py,sha256=wORyW5AkgsniK0TZcrOrvm_P7Q4Mr-fH-4S6qj9XcgA,6137
21
21
  alissa/tools/github/devloop/webui/auth.py,sha256=g9YEDao9nKt-cBEENp7vFqwJ-T8olN6uLiCzVIN8yhA,7890
22
22
  alissa/tools/github/devloop/webui/page.py,sha256=4OD3e01ByvVM4Wo04AZyFuyOYTFEMT2lagktZAvyCKk,30862
23
- alissa/tools/github/devloop/webui/server.py,sha256=YuUmcng5qdWqSurcuyxH3FKKkBBfrpDtvtZLidJOGiU,26226
24
- alissa/tools/github/devloop/webui/sources.py,sha256=JJXJEkLKh2qRbpTaIyQwCTu90DuKq_Z3nz6DwLhzlr4,38900
23
+ alissa/tools/github/devloop/webui/server.py,sha256=3aGYGesjUSrPrgcpfy-oYDefBNKnAj3lGfRzwsRkv_I,27031
24
+ alissa/tools/github/devloop/webui/sources.py,sha256=Uw_2B5QGSmW6f66WfVbQb6yLLYmSv6uuF7HNJ5OSr_I,39314
25
25
  alissa/tools/github/devloop/webui/sysinfo.py,sha256=ptlogH9alFv0vZ-pQfyUaA3w-K7LQS3tQHGv0d8Tdlk,9649
26
- alissa_tools_github_devloop-0.8.44.dist-info/licenses/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
27
- alissa_tools_github_devloop-0.8.44.dist-info/licenses/NOTICE,sha256=FKstNs3uw_tQod4_1XcFLD1FUTJQXf4FC0oXJAOA_HE,260
28
- alissa_tools_github_devloop-0.8.44.dist-info/METADATA,sha256=s-GkeBOMMoSQatUPgO0Hg_HgIyoe5TlcNi-8D-NwSYQ,12222
29
- alissa_tools_github_devloop-0.8.44.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
30
- alissa_tools_github_devloop-0.8.44.dist-info/entry_points.txt,sha256=8aC7Ajzx4bu9ZRR37a40IDfuStacvZP5KsAxY1dbFKU,145
31
- alissa_tools_github_devloop-0.8.44.dist-info/top_level.txt,sha256=DodjDg-l-TWQnlxG-Vuc3G5sB4O7cWGQwe6XrVx3u-A,7
32
- alissa_tools_github_devloop-0.8.44.dist-info/RECORD,,
26
+ alissa_tools_github_devloop-0.8.46.dist-info/licenses/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
27
+ alissa_tools_github_devloop-0.8.46.dist-info/licenses/NOTICE,sha256=FKstNs3uw_tQod4_1XcFLD1FUTJQXf4FC0oXJAOA_HE,260
28
+ alissa_tools_github_devloop-0.8.46.dist-info/METADATA,sha256=0V0tseoJcEXAfIvuBYJmDowXYvWG0FaMgnUNbASbxVE,13628
29
+ alissa_tools_github_devloop-0.8.46.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
30
+ alissa_tools_github_devloop-0.8.46.dist-info/entry_points.txt,sha256=8aC7Ajzx4bu9ZRR37a40IDfuStacvZP5KsAxY1dbFKU,145
31
+ alissa_tools_github_devloop-0.8.46.dist-info/top_level.txt,sha256=DodjDg-l-TWQnlxG-Vuc3G5sB4O7cWGQwe6XrVx3u-A,7
32
+ alissa_tools_github_devloop-0.8.46.dist-info/RECORD,,