TILs - Fueling Curiosity, One Insight at a Time

At Codemancers, we believe every day is an opportunity to grow. This section is where our team shares bite-sized discoveries, technical breakthroughs and fascinating nuggets of wisdom we've stumbled upon in our work.

Published
Author
user-image
Ashwani Kumar Jha
Senior System Analyst
Nx can set up AI coding agents for our monorepo in one command

Nx has a built-in command that configures AI agents (Claude Code, Cursor, Copilot, Codex, Gemini) to understand our workspace:

npx nx configure-ai-agents
What it sets up
• Nx MCP server: gives the agent our real project graph (which apps and libs exist and what depends on what) plus up-to-date Nx docs
• AGENTS.md / CLAUDE.md: Nx guidelines so agents use correct, current Nx commands instead of guessing
• Agent skills: teach the agent to run tasks properly (nx run, nx run-many, nx affected) and to scaffold with Nx generators instead of creating files by hand
Few more useful commands

npx nx configure-ai-agents --check=all

# Set up specific agents without the interactive prompt
npx nx configure-ai-agents --agents=claude,cursor,copilot
`` **It’s not tied to one tool** • AGENTS.md is the shared instruction file most agents read (Cursor, Copilot, Codex, Gemini, and Claude Code since v2.1.277) • Tool-specific files (CLAUDE.md, .claude/, .cursor/`, etc.) only affect their own tool and sit side by side without conflict
#nx
Published
Author
user-image
Ashwani Kumar Jha
Senior System Analyst
Attach files in Claude Code with the normal Mac file picker

Claude Code in the terminal has no attach button. We can drag files in or paste images with Ctrl+V, but picking several files from different folders is awkward. We can add a small /attach command that opens the standard macOS file picker and passes the selected files to Claude.

How it works
• A custom slash command, ~/.claude/commands/attach.md, runs a short AppleScript before your message is sent.
• The AppleScript opens the native Mac file picker and returns the paths of the files you select.
• Claude opens those files with its Read tool. Text, code, images, PDFs and notebooks all work.
Setup
Paste this into Terminal:

cat > ~/.claude/attach.applescript <<'EOF'
try
set termApp to path to frontmost application as text
tell application termApp
activate
set picked to choose file with prompt "Choose files to attach" with multiple selections allowed
end tell
set out to "Attached files:"
repeat with f in picked
set out to out & linefeed & "- " & POSIX path of f
end repeat
return out
on error
return "No files attached."
end try
EOF

cat > ~/.claude/commands/attach.md <<EOF
---
description: Pick files with the macOS file picker and attach them
argument-hint: [what you want to ask about the files]
allowed-tools: Bash(osascript $HOME/.claude/attach.applescript), Read
---
!osascript $HOME/.claude/attach.applescript

Read every file listed above, then respond to: $ARGUMENTS
If a file can't be read (e.g. .docx, .zip), say so. If no files were attached, just say so.
EOF
`` **Usage** 1. Start a new claude session. Commands only load at startup. 2. Run /attach explain this error or /attach compare these two screenshots`. #claude
Published
Author
user-image
Satya
Enable voice assistant in claude code using /voice .
Once enabled, long press the spacebar and start speaking.

#claude-code
Published
Author
user-image
Adithya Hebbar
System Analyst
Changesets: version bumps that write their own changelog

Changesets is a tool for versioning and publishing npm packages. Instead of editing package.json versions by hand and writing the changelog from git log at release time, you write down what changed as part of the change itself.

Set it up:

Code

npx @changesets/cli init


You get a .changeset/ folder and a config file. Then whenever you do something worth releasing:

Code

npx changeset


It asks which packages changed, whether it's patch/minor/major, and for a one-line summary. Your answers land in a markdown file in .changeset/ with a randomly generated name like tidy-pugs-shake.md:

Code

---
"@myscope/core": minor
"@myscope/cli": patch
---

Add a `--dry-run` flag to the publish command


Frontmatter says which packages get bumped and by how much, the body is the changelog entry. It's a plain file, so you can also skip the prompt and write one yourself, or edit it later if the wording was bad. Commit it with the code. These files pile up as you work, one per change.

Release time:

Code

npx changeset version   # eats the markdown files, bumps versions, writes CHANGELOG.md
npx changeset publish   # publishes whatever isn't on npm yet, and tags it


changeset version is the useful bit in a monorepo. Bump @myscope/core a minor, and @myscope/cli that depends on it gets bumped too, with the internal dependency range updated.

#changesets #npm #monorepo #javascript
Published
Author
user-image
Adithya Hebbar
System Analyst
🚀 Verdaccio: A local npm registry
Verdaccio is an npm registry that runs on your laptop. You can publish to it and install packages from it just like you would with npm, and you get the same tarball npm would have served.
If Verdaccio doesn't have a package, it fetches it from the public npm registry and caches it, so the rest of your dependencies continue to install normally.

Start Verdaccio:

Code

npx verdaccio # listens on http://localhost:4873


Create a local user before publishing:

Code

npm adduser --registry http://localhost:4873


Publish your package:

Code

npm publish --registry http://localhost:4873


Install it somewhere else:

Code

npm install my-package --registry http://localhost:4873


There's also a web UI at http://localhost:4873 where you can see everything you've published.

Passing --registry on every command gets old. For scoped packages, you can add one line to the consuming project's .npmrc:

Code

@myscope:registry=http://localhost:4873


Now @myscope/* resolves from your local Verdaccio registry, while everything else continues to use npm.
Just remember to remove the line when you're done. Otherwise, you'll eventually wonder why npm install is failing on a machine where Verdaccio isn't running. 😄

#npm #nodejs #verdaccio #javascript
Published
Author
user-image
Satya
Expose a Kubernetes cluster workload to the public internet using Tailscale Funnel.
Our cluster is tailnet-only (nothing public), which breaks inbound webhooks , e.g. Slack Event Subscriptions can't reach a private host.
Funnel (via the Tailscale k8s operator) exposes a single endpoint publicly over HTTPS at https://<name>.<tailnet>.ts.net cert auto-provisioned, everything else stays private + outbound-only.
It's just an Ingress:

Code

annotations: { tailscale.com/funnel: "true" }
ingressClassName: tailscale


Useful for: Slack/GitHub/Stripe webhooks, OAuth callbacks, demo links , expose one path, no public LB, no firewall hole.
Gotcha: needs funnel enabled in the tailnet ACL.
Link: https://tailscale.com/docs/kubernetes-operator/ingress/expose-workload-to-internet
#tailscale #k8s #slack #infra
Published
Author
user-image
Satya
move apps in fly from one org to another using
fly apps move <app-name> --org <target-org>

Note: Moves app as well as the secrets automatically .

#fly
Published
Author
user-image
Syed
Dolt Workbench. Local UI for Beads Issues

Dolt Workbench is a SQL UI tool that connects to our local Dolt database. We can browse issues, run queries, and see commit history and more

How to connect:

1. Make sure your Dolt server is running:

sql

bd dolt start
  bd dolt status    # note the port number


2. Open Dolt Workbench and create a new connection:

sql

Connection Name: <name-here>
  Type: MySQL/Dolt
  URL: <mysql://[email protected]>:<port>/beads


3. Leave password empty. Click "Launch Workbench".

What we can do:
• Browse tables: issues, dependencies, events, comments, config
• Run SQL queries: same as bd sql but with a visual editor
• View commit log: see every change made to the database
• Edit rows: update issue fields directly from the spreadsheet UI
• View diffs: compare changes between commits
#beads #dolt
Published
Author
user-image
Syed
Beads (bd) uses Dolt as its database backend. A SQL database with Git-like version control. Issues are stored locally in .beads/dolt/ and synced to GitHub via invisible refs (refs/dolt/data).

Setup (fresh clone)

Code

brew install beads
sudo bash -c 'curl -L https://github.com/dolthub/dolt/releases/latest/download/install.sh | bash'
mkdir -p .beads/dolt
dolt clone git+https://github.com/<org>/<repo>.git .beads/dolt/beads
bd dolt start
bd list


Daily commands

Code

bd ready                  # see available work
bd list                   # all issues
bd show <id>              # issue details
bd create --title="..." --type=task --priority=2  # create issue
bd update <id> --claim    # claim work
bd close <id>             # complete work


Sync with team

Code

bd dolt pull              # pull teammate's issues
bd dolt commit            # commit pending changes
bd dolt push              # push your issues


#dolt #beads
Published
Author
user-image
Syed
NDJSON (Newline Delimited JSON): A file format where each line is a valid, independent JSON object, separated by a newline character (\
)

Regular JSON wraps everything in an array:

Code

[
    {"name": "Alice"},
    {"name": "Bob"}
  ]


NDJSON is one object per line, no wrapper:

Code

{"name": "Alice"}
  {"name": "Bob"}


Didn't know this existed. Turns out it's really useful for:
- Log files — each event is one line, easy to grep
- Streaming APIs — send objects as they're ready, don't wait for the full array
- Large datasets — process line by line without loading everything into memory

Also called JSONL (JSON Lines). Same thing. Claude Code uses this to send MCP messages over stdio
#json
Published
Author
user-image
Nitturu
Ruby hashes treat strings and symbols as different keys.
But JSON data (JWT, APIs) always comes with string keys.

Ruby

decoded = { "user_id" => 1 }

decoded[:user_id]   # => nil ❌
decoded["user_id"]  # => 1


This silently breaks code when Rails-style symbol access is used.

We can use HashWithIndifferentAccess which removes this mismatch:

Ruby

payload = HashWithIndifferentAccess.new(decoded)

payload[:user_id]   # => 1
payload["user_id"]  # => 1


You no longer care whether keys are strings or symbols. Rails params works the same way internally.

HashWithIndifferentAccess is a class provided by ActiveSupport, so it exists only in Rails, not in core Ruby. It internally normalizes all keys (by storing them as strings) while allowing access using either symbols or strings. It’s designed specifically for boundary data—like JSON responses, JWT payloads, and request params—where key formats are inconsistent. By removing the need to care about key types, it prevents subtle nil bugs without forcing changes in how the rest of the code is written, which is why Rails uses it internally for params

#Rails
Published
Author
user-image
Satya
When two models are associated—for example, a Chat that has many Messages—you can create a message using @chat.messages.build instead of instantiating Message.new. This automatically sets the chat_id on the message, so it’s never niland the association is correctly maintained

#ActiveRecord #Rails
Published
Author
user-image
Syed
What is Framing in WebSockets?
Framing is how WebSocket data is split, structured, and transmitted over the wire. Framing is protocol-level, not app-level

WebSockets do not send raw strings or JSON directly. Instead, every message is wrapped inside WebSocket frames.

Each WebSocket frame contains:
• FIN bit – is this the final frame?
• Opcode – what type of data?
• Payload length
• Masking key (client → server)
• Payload data
Frame Types
Data frames
• Text frame → UTF-8 text (JSON, strings)
• Binary frame → raw bytes (files, protobuf)
Control frames
• PING → check if connection is alive
• PONG → response to ping
• CLOSE → graceful shutdown
#websockets #client #server
Published
Author
user-image
Swasthik
Cron in GitHub Actions
In GitHub Actions, you can schedule workflows using cron syntax inside the on.schedule field. It uses UTC time format.
Example:

Code

on:
  schedule:
    # Runs at 5:30 AM and 5:30 PM UTC every day
    - cron: '30 5,17 * * *'
    # Runs every 15 minutes
    - cron: '*/15 * * * *'
    # Runs every Monday at 9:00 AM UTC
    - cron: '0 9 * * 1'


Cron format:
┌───────── minute (0–59)
│ ┌─────── hour (0–23)
│ │ ┌───── day of month (1–31)
│ │ │ ┌─── month (1–12)
│ │ │ │ ┌─ day of week (0–6, Sun=0)
│ │ │ │ │
* * * * *

#GitHubActions #DevOps
Published
Author
user-image
Swasthik
Syntax Highlighting & Language Detection with Highlight.js

It's a lightweight JS library that automatically highlights and detects code syntax on web pages — no need for manual markup.

Installing Highlight.js

JavaScript

pnpm add highlight.js


Detect Language & Highlight

JavaScript

import hljs from 'highlight.js';
import 'highlight.js/styles/atom-one-dark.css';

const code = `function sum(a, b) { return a + b; }`;
const { value, language } = hljs.highlightAuto(code);

console.log(language); // e.g. "javascript"
console.log(value); // highlighted HTML


• You can pass value to your React component and render it inside a code element with the hljs class for styling.
Highlight.js automatically detects the language and highlights it — perfect when your app handles multiple code types dynamically.

#JavaScript #Frontend #HighlightJS
Published
Author
user-image
Vaibhav Yadav
Senior System Analyst
Clean up merged Git branches easily

You can quickly delete all local branches that have already been merged into your current branch (like master or main) using a single command:


Code

git branch --merged | egrep -v "(^\\*|master|main)" | xargs git branch -d


This command lists all merged branches, filters out the current and main branches, and deletes the rest safely. Perfect for keeping your local repo tidy after merging multiple feature branches.

#git #github #vcs
Published
Author
user-image
Puneeth
Understanding the Difference Between Regular Imports and .forRoot() in NestJS

In NestJS, modules can be imported in two main ways — but they behave differently:

1 Regular Imports
• Example imports: [ProjectsModule, UserModule]
• These are simple — they bring in the module’s exports (like services or guards).
• They don’t need any setup or configuration.
2.1 forRoot() Imports
• Example imports: [SlackModule.forRoot(dbClient)
• These are special. They let a module set itself up with configuration or dependencies (like a DB client, API key, etc.).
• They create a global instance that’s initialized once across the app.
2.2 forRootAsync()
• Used when setup depends on async config — like environment variables.
• Example :

Code

PgBossModule.forRootAsync({
  useFactory: async (config) => ({
    connectionString: config.get('DATABASE_URL'),
  }),
  inject: [ConfigService],
});


Conclusion
• Regular imports are sufficient when we just need access to a module’s services or exports.
• forRoot() is useful when a module requires initial configuration or dependencies.
• forRootAsync() is ideal when configuration depends on asynchronous operations, such as reading environment variables or fetching secrets.
#NestJS #import_module
Published
Author
user-image
Syed
getBoundingClientRect() A crucial DOM method that gives us an element's position and size relative to the viewport, not the document.

Example:

JavaScript

const rect = container.getBoundingClientRect();


What getBoundingClientRect() returns?

JavaScript

{
  top: 100,      // Distance from viewport top to element top
  left: 200,     // Distance from viewport left to element left  
  right: 400,    // Distance from viewport left to element right
  bottom: 300,   // Distance from viewport top to element bottom
  width: 200,    // Element width
  height: 200,   // Element height
  x: 200,        // Same as left (for compatibility)
  y: 100         // Same as top (for compatibility)
}


Real Usage Example: Image Comparison Slider:

JavaScript

// User drags the slider to compare two images
handleDrag(event) {
  const container = this.getContainer(); // The image container
  const rect = container.getBoundingClientRect();
  
  // Convert global mouse position to slider position
  const x = event.clientX - rect.left;  // Mouse X relative to container
  const percentage = this.calculatePercentage(x, rect.width);
  
  // Update slider position (0-100%)
  this.sliderPosition = percentage;
  this.updateSliderPosition(percentage);
}


Step-by-Step Breakdown:
1. User drags mouse → event.clientX = 350 (global position)
2. Get container bounds → rect.left = 200 (container starts at 200px from viewport left)
3. Calculate relative position → 350 - 200 = 150px (mouse is 150px from container's left edge)
4. Convert to percentage → 150 / 400 = 37.5% (150px out of 400px container width)
5. Update slider → Move slider to 37.5% position
getBoundingClientRect() is the bridge between global coordinates and element-relative coordinates.

#CCT1JMA0Z #stimulus
Published
Author
user-image
Syed
When we rewrite git history (like squashing commits, interactive rebase), our local branch diverges from the remote branch.
A regular git push will be rejected because the histories don't match. The first option is git push --force, which overwrites the remote branch with our local version.

The Usual Approach: git push --force

Code

# DANGEROUS - can overwrite other people's work
# We do it everyday though
git push --force origin <feature-branch>


The Safer Alternative: git push --force-with-lease

Code

# SAFER - includes safety checks
git push --force-with-lease origin feature-branch


How --force-with-lease works:
• Checks if the remote branch has moved since we last fetched it.
• Only overwrites if the remote is exactly where we expect it to be.
• Fails safely if someone else has pushed commits we don't have locally.
• Prevents accidental overwrites of other people's work.
PS: Might sound simple, but helps a lot when working with bigger teams.

#git #github
Published
Author
user-image
Nived
If a port (say 3000) is already in use, you can check what’s running on it with:

Code

lsof -i :3000


Here, lsof stands for “List Open Files” — and in Unix/Linux, everything is a file, including network sockets.

So this command lists all processes that have files (or ports) open.
Example output:

Code

COMMAND   PID  USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
node     1234  nived  22u  IPv6  0x...      0t0  TCP *:3000 (LISTEN)


Now you can see the PID (Process ID) of the program using the port.

To stop it, run:

Code

kill 1234


The kill command sends a signal to a process — by default, it’s SIGTERM (Signal Terminate), which politely asks the process to shut down.
If the process refuses to die, you can forcefully stop it with:

Code

kill -9 1234


Here, -9 represents the SIGKILL (Signal Kill) — which immediately ends the process without cleanup.

#unix

Showing page 1 of 42

Your competitors are already using AI.
The question is how fast you want to unlock the value.

Don't know where to start?

AI is everywhere but it's unclear which investments will actually move your metrics and which are expensive experiments.

Your data isn't ready

Most AI projects fail at the data layer. Pipelines, quality, access all need work before LLMs can deliver value.

Internal teams are stretched

Your engineers are shipping product. They don't have capacity to also become AI specialists with production-grade experience.

Legacy systems block everything

Aging, undocumented codebases make AI integration slow, risky, and expensive. They need to move first.

Don't worry. We've got you covered.

Start with the audit.