网友语录 - 第81期 - 属于我们的当下只有这么一个。即便是有许多缺憾的它,依然有很多值得我们珍惜和守护的人与事物

这里记录我的一周分享,通常在周六或周日发布。


如月中天

#神秘的摘抄 没有人会知道自己最终会走向何方,选择可以引申出千万种可能,但是属于我们的当下只有这么一个。即便是有许多缺憾的它,依然有很多值得我们珍惜和守护的人与事物


Marskay 他不喜欢讲话,但是做了老师不得不讲。他更希望的生活状态是只和很少人说话,写完一本书接着写另一本。等他退休了,他想去山里住,种竹子和芭蕉。竹子干净,它的竿上有细细的绒毛,没有别的东西,在竹子上写字好看,芭蕉叶上写字也好看。春天,竹子生长的声音很大。夜里,雨打在芭蕉上的声音也很大


闽南大翠花 任何时候都不要期望着自己能够改变任何人,就像天正在下雨,你可以选择撑伞或叫出租车,唯独不能跟雨较劲


mywaiting Cloudflare Zero Trust 有一个 Free 方案,支持最多 50 个用户。对于个人远程运维、管理家庭服务器或实验室设备来说,这个额度绰绰有余。而且支持 Tunnel 代理 SSH,直接浏览器访问就能操作服务器 SSH 而无需暴露任何端口配置,完美啊!(听起来和tailscale有一拼,但我并不知道这个产品,有时间拿来试试


桌面宠物

dimlau 2026-05-04

Codex Pet 是 OpenAI 为 Codex 推出的一项桌面端体验功能,本质上是一个像素风的桌面互动陪伴宠物。

我突然想起前两天偶然翻到的 2007 年的一篇文章里写到:

如果能有一个桌面宠物——没有程序边框的,宠物活动的时候也不影响进行其它程序操作的——集成一些实用的功能,并且带有一点哪怕很粗糙的人工智能、情感养成系统等等,应该是很酷很科幻的一件事吧!- 订阅的 feed 或者你的邮件有更新了宠物会提醒你查看;

  • 通过“阅读”你订阅的文章,宠物也能学习一些日常用语和你对话;
  • 虽然很讨厌但是它还是会不时的提醒你“对着电脑很久了,该歇歇了”;
  • 你长时间没有对它操作时,它会通过网络去同样养宠物的桌面串门,虽然很冒昧,我觉得可爱宠物入侵一般不会有人怪罪吧?被串门的电脑用户,可以选择喂来访宠物或者驱走它,同时你的宠物串门完毕后会根据别人的招待程度留下一份记录,你可以通过这份记录得知一些陌生人的网站、blog、feed 地址,并知道这个人对你的宠物干了什么;
  • 你对宠物的照料决定了它会不会离家出走不回来;
  • ……

原谅我 24 岁时幼稚的想法和文笔,毕竟一转眼,那已经是二十年前的事了。


(关于AI)Sam 越是频繁使用,越要有意识地给自己设一些边界——尤其是涉及情绪、关系和重大决策时。比如换一种问法:与其问"我是不是对的",不如问"请指出我叙述中片面的地方""请从对方视角重新解释这件事""请不要安慰我,直接指出我的推理漏洞"。


为什么要写作

作者 Limboy

Paul Graham 在 Writes and Write-Nots 中预见了一个分裂为「写作者」与「非写作者」的世界。他说,这远比听起来的要危险, 因为它本质上是一个「思考者」与「非思考者」的世界。文中他做了一个类比:

在工业革命之前,大多数人的工作都能强健体魄。现在如果你想变得强壮,你得主动去健身。强壮的人依然存在,但只有那些"选择"强壮的人才会如此。写作(即思考)正在经历同样的转变。

为什么写作跟思考会强相关?因为人的思维是网状的、跳跃的,而文字是线性的。写作本质上是一个将混沌思想进行结构化的过程,它强迫你梳理逻辑、暴露盲区、发现矛盾。我们时常以为自己想明白了,但很可能只是产生了一种理解的幻觉。脑子里那些「感觉很清楚」的想法,一旦要落到纸上,就会变得支离破碎。所以写作最深层的价值不在于输出,而是倒逼你更清晰地思考。

当你以为自己已经足够了解某个话题,觉得没有必要再写出来时,创作冲动就在这种「我已经懂了」的幻觉中消解了。只有当真正坐下来写的时候,才会发现自己的理解漏洞百出。

假如你养成了写作的习惯,你和世界的关系会发生一个微妙但深刻的转变:一切经历都变成了素材。这会带来一种视角转换:你不再只是经历生活,同时也在观察和收集。哪怕是生活中的低谷、摩擦与困境,经过文字的咀嚼,也能转化为绝佳的素材。我在李娟的文字中深刻地感受到了这点,当她用平实的文字包裹真挚的感情来描写她与妈妈、妹妹、外婆还有「叔叔」发生在阿勒泰的点点滴滴时,不禁感慨,会写作真好。

如果不写作,今天想通的道理,三个月后可能就忘了。强迫自己写下来,可以看到认知轨迹的变化:回头看几年前写的东西,哪些观点被推翻了,哪些直觉被验证了。这种「和过去的自己对话」的体验,是其他方式很难替代的。更重要的是,这些文字是只属于你自己的数据,它们构成了你独一无二的思想档案,在 AI 时代,这些数据投喂给 AI 后,可以让它更懂你,成为真正个性化的思维伙伴。

如果持续写作,你写下的每一篇文章,都是一个可以被搜索、被发现、被传播的节点。短期来看,它帮你完成了思考;长期来看,它有可能产生意想不到的连接 -- 吸引志同道合的人、打开新的机会、甚至改变你的职业轨迹。持续写作会提升驾驭文字的能力,这会带来一种掌控感和踏实感。把心里想的东西完整、细腻地写出来,这种满足感是刷短视频无法比拟的。

写作除了能帮你整理思维、记录生活,它还是一项通用且重要的能力。 37Signals 在 Getting Real 里有一章专门聊了写作: Hire good writers

如果你在几个候选人之间犹豫不决,请务必聘用那个文笔更好的人。无论对方是设计师、程序员、市场营销还是销售人员,优秀的写作能力都会显现出其价值。因为简洁、高效的写作与编辑,往往意味着同样简洁、高效的代码、设计、邮件及日常沟通。

这本书出版于 2006 年,距今已有 20 年,这个观点过时了吗?我觉得没有,一个擅长写作的人依旧能在职场中获得额外的加分。

既然写作是一件收益很高的事,为什么真正写作的人却越来越少了呢?因为它本质上就是一件难事。

首先,它要求你同时做好两件截然不同的事:思考和表达。想要写得好,必须先想得清,而清晰的思考绝非易事,它需要你静下心来去啃透问题的方方面面,确保没有错误和遗漏,然后再形成一个大纲。但如果按照这个大纲去写,又容易掉进另一个陷阱:忽视了读者视角。gwern 在 First, Make Me Care

这篇文章专门讲了这个问题,他以威尼斯这座城市为例,展示了读者视角和非读者视角在内容呈现上的差异。前期准备工作完成后,精准的语言表达也极具挑战,如果没有经过刻意训练,文字和真实想表达的内容之间就可能出现错配。

除此之外,还容易被自己的品味打压。作为阅读者,你的品味往往远高于你当前的写作能力,当你写出第一稿时,你的高品味会立刻觉察到它的粗糙,这种落差会带来极大的自我怀疑和挫败感。要缩小这个差距,唯一的办法就是持续创作。_why 的这段话,用在写作上也一样合适:

当你停止创造,你的才能就不再重要,你所拥有的只剩下你的品味。而品味会裹挟你,让你排斥他人、变得狭隘。所以,去创造吧。

除了过程痛苦,结果的不确定性也会劝退很多人。它不像学编程,可以通过做 App 来营收,也不像内容创作(视频、播客)那样有相对成熟的变现路径。写作的回报是高度延迟和间接的:思维更清晰、表达更精准、认知更深刻 -- 这些好处真实但不可量化,而且很难在短期内看到立竿见影的效果。所以除非你从写作的过程本身获得满足,否则很难坚持下去。

既然写作这么难,又这么重要,而 AI 又很擅长文字,可以让 AI 来代劳吗?这是我们下意识会想到的解决方案,但写作的过程就是思考的过程,而思考是不应该被外包的。让 AI 替你写,等于让别人替你想 -- 你得到了一段文字,但失去了思考本身带来的所有价值

虽然不应该让 AI 来写作,但可以让 AI 来帮助自己完善思维盲区和误区,前提是得先有自己的想法、框架、初稿,然后让 AI 在此基础上帮你查漏补缺、拓展视角。

这就是为什么要写作,它是无需天赋的思维健身,具有普遍性、重要性、高门槛、复利效应的特性,而且只要你愿意,可以一直写下去。

原文


你的职业规划就是三步:(1)让自己擅长某事,(2)设法从中赚钱,(3)尽可能久地做下去


Anyone can become angry, that is easy ...

but to be angry with the right person, to the right degree, at the right time, for the right purpose, and in the right way ... this is not easy.

—ARISTOTLE

If I am not for myself, who is for me?

If I am only for myself, what am I?

If not now, when?

—HILLEL

Illusions are the truths we live by until we know better.

—NANCY GIBBS

摘自 The explosive child - A new approach for understanding and parenting easily frustrated, chronically inflexible children

Setting `CLAUDE_CONFIG_DIR` in `.env`? It silently loses to your shell.

Trying to isolate a child process by setting an env var in .env, only to find the value inherited from your shell wins instead. dotenv by default does not overwrite existing process.env values.

This bit me in HelloEDi. I wanted to spawn claude -p with CLAUDE_CONFIG_DIR pointing at an isolated dir so the user's global ~/.claude/CLAUDE.md (which carries a personal persona) wouldn't leak into the assistant. .env had:

CLAUDE_CONFIG_DIR=/home/davidw/.helloedi/claude-home

But my shell already exports CLAUDE_CONFIG_DIR=/home/davidw/.claudeh for my own Claude Code setup. dotenv loaded .env, saw the var already set, did nothing. The server's process.env.CLAUDE_CONFIG_DIR was still the shell's value. The spawned claude happily read the persona from there. The assistant introduced itself with the wrong name.

The fix is to never put the canonical var name in .env when your dev environment is likely to export it. Use a project-internal name and translate on spawn:

# .env
EDI_CLAUDE_CONFIG_DIR=/home/davidw/.helloedi/claude-home
// claudeRunner.js
const dir = process.env.EDI_CLAUDE_CONFIG_DIR ?? defaultDir;
spawn('claude', args, {
  env: { ...process.env, CLAUDE_CONFIG_DIR: dir },
});

Now the shell's CLAUDE_CONFIG_DIR stays in process.env (harmless to the parent), .env's EDI_* value is read without collision, and the child gets the right CLAUDE_CONFIG_DIR because we set it explicitly in the spawn env.

dotenv does have { override: true }, but flipping it globally affects every variable in .env — including ones you actually want the shell to win on. The renamed-variable approach is scoped and self-documenting.

The shape of this trap is general: whenever a name in .env overlaps with one your shell may already export, the child sees whichever value won at parent startup, not the one you wrote. Pick names you control, or be explicit at spawn time.

Your .bashrc bails out before agents see PATH — fix it with BASH_ENV

The first non-comment line in most .bashrc files is this:

[ -z "$PS1" ] && return

It means: if this shell isn't interactive, do nothing. Innocent-looking, but it silently breaks any agent that shells out via bash -c '...' — the child shell gets no PATH additions, no cargo env, no API keys you carefully exported in .bashrc.custom. Your terminal works fine, your CLI agents don't, and the failure mode is some bare command name "not found" that has nothing to do with the actual problem.

The fix is to split the file by purpose (env vs interactive), not by file (.bashrc vs .bashrc.custom), and use BASH_ENV as the loader for non-interactive shells.

Split env from interactive

Pull every export, every PATH= mutation, every . ~/.cargo/env and . ~/.bashrc.secret out of .bashrc.custom and put them in a new file. Call it ~/.bashrc.env. It must be idempotent and produce no output (no echo, no which).

# ~/.bashrc.env — env-only, safe for both interactive and non-interactive shells.
export FNM_DIR="$HOME/.local/share/fnm"
export PNPM_HOME="$HOME/.local/share/pnpm"

case ":$PATH:" in
  *":$FNM_DIR:"*) ;;
  *) PATH="$FNM_DIR:$PATH" ;;
esac
# ... more PATH cases ...

export PATH

[ -f "$HOME/.cargo/env" ]      && . "$HOME/.cargo/env"
[ -f "$HOME/.bashrc.secret" ]  && . "$HOME/.bashrc.secret"

# This is the lever — child non-interactive bashes will source this file.
export BASH_ENV="$HOME/.bashrc.env"
…more

Stop opening admin cmd just to mklink — git-bash can do real symlinks

If you've been alt-tabbing to an admin cmd window to run mklink every time you wanted a symlink on Windows, there's a much cleaner way nobody seems to mention. Two settings, set them once, forget about it.

The reason ln -s in git-bash silently turns into cp by default is two unrelated barriers stacked together:

  1. Windows non-admins can't create symlinks unless Developer Mode is on.
  2. MSYS (git-bash's runtime) doesn't even try to create native symlinks without the MSYS env var set.

Fix the first once with admin, fix the second in your shell profile.

# Run once in admin PowerShell, or use Settings → Privacy & Security → For developers → Developer Mode
Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\AppModelUnlock" `
  -Name "AllowDevelopmentWithoutDevLicense" -Value 1 -Type DWord
# In ~/.bashrc
export MSYS=winsymlinks:nativestrict

nativestrict makes failures loud — ln -s errors out instead of silently copying when symlinks aren't supported. Avoid winsymlinks:native (the lenient sibling) — its silent fallback is exactly the trap you're trying to escape. Avoid winsymlinks:lnk entirely; .lnk shortcuts aren't symlinks anything else recognizes.

Open a new git-bash window and verify:

ln -s ~/.bashrc /tmp/test-link
ls -la /tmp/test-link

If the line starts with l, it's a real symlink that git, Linux tools, GNU stow, and your dotfiles install.sh all treat consistently across platforms. If it starts with -, either the env var didn't reach the new shell or Developer Mode didn't actually toggle.

Bonus: Developer Mode also unlocks Windows 11's native sudo command, so you can stop alt-tabbing to admin terminals for quick one-offs entirely.

WT keeps opening as Administrator? Check settings.json before the registry

Your Windows Terminal default tab is opening with admin privileges and you don't remember asking for it. The intuitive guess is the AppCompat Layers registry — that's where ticking "Run as administrator" on a shortcut's compatibility tab gets stored, and it persists across reinstalls. Worth checking, but it's the second place to look for WT.

The first place is settings.json:

{
    "commandline": "C:\\Program Files\\Git\\bin\\bash.exe",
    "elevate": true,
    "guid": "{17c3a2bf-...}",
    "name": "Git Bash"
}

WT 1.18+ added elevate as a per-profile flag. When it's true and that profile is also pointed to by defaultProfile, every new window silently opens elevated — no UAC prompt at the tab level because the elevation happened at WT launch. Delete the line, restart WT, done.

If you still want an admin tab on demand, add a second profile with a fresh GUID and a different name (e.g. "Git Bash (Admin)") that keeps elevate: true. You get a dropdown choice and a real UAC prompt when you actually need it, instead of unconditional elevation on every launch.

To rule out the registry side as well:

Get-ItemProperty -Path "HKCU:\Software\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers"
Get-ItemProperty -Path "HKLM:\Software\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers"

Each property name is an exe path; a value containing RUNASADMIN means that program is forced to elevate every launch. To clear one, prefer the GUI — right-click the exe → Properties → Compatibility → uncheck "Run this program as administrator" — over editing the registry by hand.

Two mechanisms, same symptom, different layers. For Windows Terminal specifically, the per-profile setting wins; check it first.