Hacker Newsnew | past | comments | ask | show | jobs | submit | qudat's commentslogin

Nice article. For easily exposing private services to the internet I’ve been using https://tuns.sh which lets you run ssh tunnels. It’s nice for a zero install solution.

I’ve done spec driven development using a bunch of markdown files. That works fine but what I have found really works is using beads: https://github.com/steveyegge/beads

I’m letting the agent help me draft the specs anyway and I found that the agent is a lot more focused when it can traverse a task tree using beads.

It’s the one spec or planning tool that I find really helps get things done without a bunch of intervention.

Another technique I employ is I require each task to be TDD. So every feature has two tasks: write tests that fail, implement feature and don’t notify me until tests complete. Then I ask the agent to tell me how to review the task and require I review every task before moving to the next one. I love this process because the agent tells me exactly what commands to run to review the task. Then I do a code review and ask it questions. Reading agent code is exhausting so I try to make the tasks as discrete and minimal as possible.

These are simple techniques that humans employ during development and I find it worked very well.

There are also times when I need to write some docs to help me better understand the problem and I usually just dump those in a specs folder.

I think spec-kit is an interesting idea but too heavy handed. Just use beads and you’ll see what I mean.

Another technique I employed for a fully vibed tool (https://github.com/neurosnap/zmx) is to have the agent get as far as possible in a project and then I completely rewrite it using the agent code purely as a reference.


Not sure why Steve re-inveted a VCS for agents, if you just use Github/Gitlab all this stuff is properly tracked and surfaced.

This sounds promising! Could you give us examples of how you give instructions to the coding agent?

To pile on, if I wanted an actual language to do config work I would prefer bash for simple scripting. I started using riverwm and I love its take on config. Use whatever language you want because configuring it is just running a sequence of riverctl commands. I found it very ergonomic since i can test config in the cli, I can just use a bash script to run commands — and some extra daemons like mako, waybar, and wlsunset

Based purely on this blog post it looks like they are trying to compete against notion but with an IDE extension

Wasn’t that Alyx?

Perhaps Alyx walked so HL3 could run.

I was hoping for a zfold

Yes. I full time code on a corne and notice only improvements to ergonomics. Once you memorize the layers it's superior because your layers can place brackets without reaching. All those special characters we use all the time are on -- or close to -- the home row.


But there are no such improvements due to this. On a bigger keyboard you can do all the same layers, but also use those extra keys for something less frequent - it still is easier to remember a physically dedicated key position for some action than via layering

i love my corne, it's such a joy to use.


Charm was my introduction into the world of ssh apps which prompted me to create https://pico.sh

SSH apps serve a similar UX to web apps which I just think is a great idea for many use cases. Needing to install a cli tool just to upload some files is tedious when you can just use rsync, sftp, piping, or even sshfs


What’s an SSH app vs a terminal app?


A terminal app is running during an interactive shell session. A ssh app basically allows you to SSH into the app, without ever ending up in the shell.

A fun example of this is https://www.terminal.shop/


Any terminal app can be an “ssh app”. There isn’t really a distinction.


When you start doing git surgery where there are commit chains that need to stay logical is where JJ starts to shine. If you are constantly editing previous commits and placing code in your working area into those previous commits and rebasing original/main.

I also really like that every change is automatically committed. It’s a great mental model once you get used to it.


Git rebase works fairly well and is somewhat uneventful, unless there are major changes happening. I do hate the experience when one file was remove in my feature branch, but main did a major refactor which affected the original file, so conflicts are a bit awkward then - but other than that, this seems like a fairly clean workflow.


Git rebase is an enormous pain in the ass.

Rebases must be done linearly. And right now! Oops, you made an error in an earlier stage of the rebase? Start over, good luck! Want to check something from earlier while you’re in the middle? Sorry, you’re in a modal state and you don’t get to use your regular git tooling.


You can just record all your changes with git commit --fixup and then do a non-interactive rebase that just applies all the changes.

You can use all the regular git tools in a rebase, in fact it would be quite useless without. You can also just jump to other branches or record a fix to a previous commit. It doesn't matter what you do in the meantime, it only cares what is the HEAD, when you call git rebase --continue, and then it only performs what commands you specify in the rebase todo. You can even change the todo list at any time.


Yes, it's certainly possible to do all those things with Git. Compared to jj, it's just much harder to do, easier to mess up, and harder to recover from if you do mess up.


I just gave you an example how it is not "much harder".


As I understood the scenario:

1. We're rewriting some commits. Let's say a chain of commits A through G. We want to make some change to commits A and D.

2. As we're editing commit D, we realized that we need to make some changes to B to match the updated A.

3. Also while editing D we realized that we want to take a look at the state in A to see how something worked there.

With jj, here's what I would do:

1. Run `jj new A`, make the changes, then `jj squash` to squash the changes in to A and propagate them through the chain.

2. Run `jj new D` to make changes. We now notice that we wanted some changes to go into B. We can make the changes in the working copy and run `jj squash --into B -i` to interactively select the changes to squash into B.

3. Run `jj new A` to create a new working-copy commit on top of A, look around in your IDE or whatever, then run `jj edit <old commit on top of D>`. Then run `jj squash` to squash the remaining changes in the working copy into D.

I think you know the steps to do with Git so I won't bother writing them down. I find them less intuitive anyway.


1. Edit changes, run `git commit --fixup=A`.

2. Edit changes for D. Also edit changes for B. Interactively `git add` changes for B. `git commit --fixup=B`.

3. Finish changes for D, then call git commit --fixup=D

Before push I run `git rebase --autosquash`, optionally `git rebase --exec='make check'` to rerun all the tests on the changed commits.

Where is that "much harder"?


Ah, I see, so you avoid interactive rebase and instead make all changes in the working copy and use `git commit --fixup` and `git rebase --autosquash` . Makes sense, but doesn't it break down when there are conflicts between the changes you're making in the working copy and the target commit? How do you adjust the steps if there were conflicts between the changes we wanted to make to A and the changes already present in B?


> Ah, I see, so you avoid interactive rebase and instead make all changes in the working copy and use `git commit --fixup` and `git rebase -i .

I wouldn't say I avoid this, I also run `git rebase -i` several times per day, and I also often use `git commit --fixup` during a rebase.

> Makes sense, but doesn't it break down when there are conflicts between the changes you're making in the working copy and the target commit?

Yes, but wouldn't this be the same in JJ, when you do your changes on top of A, and later squash them into D? If you don't want to have the changes, you can also checkout D and do the changes there. Then you have two options:

- `git commit --fixup`, later do `git rebase`

- `git commit --amend`, and `git rebase --onto`

Most times I do the thing described earlier and just solve the conflicts, because that's just a single command. Also when its only a single case, I use `git stash`. (The workflow then is do random fix, git stash, then figure out where these should go, git rebase)

> How do you adjust the steps if there were conflicts between the changes we wanted to make to A and the changes already present in B?

I just resolve them? I think I don't understand this question.


> I just resolve them? I think I don't understand this question.

In order to make changes to commit A when there are conflicting changes in B, I was thinking that you would have to use interactive rebase instead because you can no longer make those changes in the working copy and use `git commit --fixup`, right? And because there will now be conflicts in commit B, you will be in this "interrupted rebase" state where you have conflicts in the staging area and it's a bit tricky (IMO) to leave those and look around somewhere else and then come back and resolve the conflicts and continue the rebase later.

> Yes, but wouldn't this be the same in JJ, when you do your changes on top of A, and later squash them into D?

The difference is that we don't end up in an interrupted rebase. If we squashed some changes into A and that resulted in conflicts in B, then we would then create a new working-copy commit on top of the conflicted B (I call all of the related commits B even if they've been rewritten - I hope that's not too confusing). We then resolve the conflicts and squash the resolution into B and the resolution gets propagated to the descendants. We are free at any time to check out any other commit etc.; there's no interrupted rebase or unfinished conflicts we need to take care of first. I hope that clarifies.


> I was thinking that you would have to use interactive rebase instead because you can no longer make those changes in the working copy and use `git commit --fixup`, right? And because there will now be conflicts in commit B, you will be in this "interrupted rebase" state

Yes.

I don't see the drawback honestly. Invoking git rebase, means I want to resolve conflicts now, when I want to do that later, I can just call git rebase later. When you want to work on top of the B with conflicts, the code wouldn't even compile, so I expect JJ, to just give you the code before the squash, right? How is this different from in Git?


The difference is that jj doesn't force you to resolve the conflict right away. I agree that you usually want to do that anyway, but it has happened to me many times that some conflict turned out to be more complicated than I had time for at the moment and I needed to work on something else for a while. When using Git, I would typically abort the rebase in such cases, which is not so bad if you have rerere enabled (I can't remember if it records any resolutions I had staged or if that's only one you commit).

Anyway, I'm just explaining how jj works and what I prefer. As Steve always says, you should use the tools you prefer :)


> When using Git, I would typically abort the rebase in such cases, which is not so bad if you have rerere enabled

Yes, I do the same. I think it's not too different. You can also commit randomly somewhere else, it is only a problem once you try to start another rebase or merge. (But I never needed to do it, I just tried it out during discussions like this.)

> Anyway, I'm just explaining how jj works and what I prefer. As Steve always says, you should use the tools you prefer :)

Sure. I'm not objecting to you using JJ, I was objecting to you stating, that it is "much harder" in Git. This is seems to be a common sentiment among JJ users, but it always seem to amount to that people bother to read the manual and understand the tool AFTER they used a VCS for years.


> it always seem to amount to that people bother to read the manual and understand the tool AFTER they used a VCS for years.

Perhaps, but I don't think that's true for me (or for Steve). I've contributed something like 90 patches to Git itself (mostly to the rebase code). To be fair, that was a while ago.

My impression is actually that many people who disagree with the sentiment that jj is much easier to use seem to have not read its manual :) Some of them seem to have not even tried it. So, the way it looks to me, it's usually the people who argue for jj who have a better understanding of the differences between the two tools.

Have you tried jj yourself and/or have you read some of the documentation?


I wanted, but it didn't compile due to needing a newer Rust compiler, than is available in my Distro. And the tutorials I found told me to run the equivalent of curl|bash, which I will not do. I don't felt like learning a new language/ecosystem just to try out another VCS, so I said, it's not worth it, I wait until it is available in my distro.

So actually no, and you have a point. :-)

I often just read "this is hard in Git" and think isn't this just this command? JJ has some nice features, but what appeals to me seems to not to be that hard to add to Git, so I will just wait a bit.


I don’t know about you, but I am tired of having to remember the dozens of simple, one-off workarounds to every single thing I want to actually accomplish.

A few months back I had to sanitize the commit history of a repo that needed certain files removed from the entire history. I could have read the manpage for `git filter-branch`, but in jj editing commits is just a normal, sane part of your workflow. It was a blindingly obvious one-liner that just used all the commands I use every day already.

Even better, it was fast. Because “edit the contents of a bunch of commits” is a first-class operation in jj, there’s no need to repeatedly check out and re-commit. Everything can be done directly against the backing repository.


I don't remember anything really, I just derive it on demand from first principles and by using autocomplete in the shell.

I don't consider `commit --fixup` to be some arcane workaround, that is basically the default to record a change to some older commit.

Editing commits is also a normal, sane part of my workflow, what else is a version control system supposed to do? I consider modifying every commit in a repo not to be that frequent, but nice if JJ supports that easily. Do you want to educate us of the command?

Git also does certain modifications entirely in memory, but when I edit some file obviously my editor needs to access it. Also I want to rerun the tests on some modified commit anyway, so to me checking it out is not some extra cost.


> Do you want to educate us of the command?

Not sure that they had in mind but you can do `jj squash --from <oldest commit with unwanted file>:: --destination 'root()' <path to unwanted file>`. That will take the changes to the unwanted file from all those commits and move them into a new commit based on the root commit (the root commit is virtual commit that's the ancestor of every other commit).


git has rerere for this usecase, jj doesn't - you have to find the conflict resolution manually in your history in this case if you made a mistake.


git has rere, but jj doesn't because its equivalent is built in. https://github.com/jj-vcs/jj/issues/175#issuecomment-1079831... is some discussion about the differences here.


Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: