Building a single-pass rich-text DSL parser (no regex)

I built this while trying to replace Markdown in my personal blog.
Markdown is convenient, but once you start adding custom syntax, parsing rules quickly become hard to reason about.

I wanted something with:

  • predictable parsing behavior
  • nested structures without ambiguity
  • extensible semantics
  • minimal syntax surface

So instead of a regex-driven approach, I implemented a single-pass recursive descent parser with no backtracking.
The parser handles three forms — inline, block, and raw — all using the same core state machine.

For example:

$$bold(hello)$$

$$info()*
This is a block tag
*end$$

Handlers are pluggable, so the grammar stays small while behavior is defined externally.
This keeps the parser focused on structure, not rendering.

The result is a lightweight DSL that works well for structured rich text, especially in static site generators or blog engines.

GitHub: https://github.com/chiba233/yumeDSL
npm: https://www.npmjs.com/package/yume-dsl-rich-text
demo: https://qwwq.org/blog/dsl-fallback-museum

The Venus Protocol Donation Attack: How a Dismissed Audit Finding Became a $2.15M Bad Debt — Twice

On March 15, 2026, Venus Protocol on BNB Chain was hit by an exploit that left it with $2.15 million in bad debt. The attack targeted the THENA (THE) token market using a donation attack — a vulnerability class so well-known it was literally flagged in Venus’s own Code4rena audit.

The protocol dismissed the finding. Then it got exploited. Again.

Here’s the anatomy of what went wrong, why three separate lines of defense failed simultaneously, and what every Compound-fork lending protocol needs to learn from this.

The Vulnerability: Donation Attacks in Compound Forks

In Compound-style lending protocols, supply caps limit how much of a given token can be deposited as collateral. But here’s the critical design flaw: most implementations only enforce this cap on the mint path — the standard deposit function that issues vTokens (or cTokens).

They don’t account for tokens transferred directly to the contract address.

// The mint path checks the supply cap
function mintInternal(uint mintAmount) internal {
    require(totalSupply + mintAmount <= supplyCap, "supply cap exceeded");
    // ... mint vTokens
}

// But a direct ERC-20 transfer bypasses everything:
// IERC20(THE).transfer(address(vTHE), amount);
// No cap check. No access control. Just... more tokens.

When tokens are sent directly to the vToken contract, the contract’s underlying balance increases without minting new vTokens. Since the exchangeRate is calculated as:

exchangeRate = (underlyingBalance + totalBorrows - reserves) / totalSupply

This inflates the exchange rate. Every existing vToken holder suddenly has more borrowing power — without any supply cap check firing.

The 9-Month Setup

This wasn’t a flash loan one-block wonder. The attacker started in June 2025:

  1. Funded via Tornado Cash: 7,447 ETH → deposited into Aave as collateral → borrowed $9.92M in stablecoins
  2. Gradual accumulation: Over 9 months, systematically bought THE tokens across multiple wallets
  3. Reached 84% of supply cap: By March 15, the attacker controlled ~12.2M THE out of the 14.5M cap

Every single one of these transactions was visible on-chain. The address had even been flagged by community members. Venus declined to act, citing decentralization.

The Execution

On March 15, 2026 at 11:00 UTC, the attacker deployed a malicious contract and executed the donation attack:

  1. Donated THE directly to the vTHE contract, bypassing the supply cap
  2. Exchange rate inflated 3.81x, massively increasing borrowing power
  3. Borrowed ~$14.9M in BTCB, CAKE, USDC, and BNB against the inflated collateral
  4. Repeated the cycle: borrowed → swapped for THE → donated → borrowed more
  5. THE price pushed from $0.263 to over $0.51 during the manipulation
  6. Total THE in Venus reached 53.23M — that’s 367% of the 14.5M supply cap

Three Lines of Defense, Three Failures

1. Supply Cap (Bypassed)

The supply cap only guarded the mint function. Direct transfers went unchecked. This is the core vulnerability.

Fix: Supply caps must account for the contract’s actual underlying balance, not just minted supply. The getCashPrior() function reads the raw token balance — any discrepancy between this and the tracked supply should trigger circuit breakers.

2. Oracle-Based Collateral Valuation (Manipulated)

The attacker’s buy-and-donate cycle artificially inflated THE’s market price alongside the exchange rate manipulation. The oracle faithfully reported the manipulated price.

Fix: TWAP oracles with longer windows, circuit breakers on rapid price movements, and collateral factor adjustments based on on-chain liquidity depth.

3. Liquidation Market (Overwhelmed)

254 liquidation bots competed across 8,048 transactions to unwind the position. Despite this aggressive competition, $2.15M in bad debt remained. The THE token’s price collapsed from $0.51 to $0.22 during the liquidation cascade — below its pre-attack level — making each successive liquidation less effective.

Fix: Gradual liquidation mechanisms (like Aave’s soft liquidation), protocol-owned liquidation reserves, and dynamic liquidation incentives that scale with position risk.

The Audit Finding That Was Dismissed

This is the most damning part. The donation attack vector was explicitly identified during Venus Protocol’s Code4rena security audit. The Venus team’s response:

“Donations are an intentional feature with no negative side effects.”

This wasn’t ignorance. It was a conscious decision to accept risk that the team didn’t fully understand.

And it wasn’t even the first time. In February 2025, Venus suffered the same class of attack on its zkSync deployment, resulting in $700K+ in bad debt. The protocol saw the same vulnerability exploited, patched the specific instance, and still didn’t address the systemic issue across all deployments.

Detection Opportunities That Were Missed

The 9-month preparation window provided multiple detection signals:

  • Single entity accumulating 84% of supply cap — visible from June 2025 onwards via position concentration monitoring
  • Tornado Cash-funded address active in protocol — visible from June 2025 via funding source analysis
  • Community flagging of suspicious address — months before exploit via governance alerts
  • Rapid position changes on attack day — March 15, 11:00 UTC via real-time transaction monitoring

None of these required sophisticated tooling. Basic position concentration alerts — “notify when any address holds >50% of a market’s supply” — would have surfaced this months in advance.

Takeaways for Builders and Auditors

If You’re Building a Compound Fork

  1. Validate supply against actual balance, not just mint accounting. Add invariant checks:
require(
    IERC20(underlying).balanceOf(address(this)) <= expectedBalance + DUST_THRESHOLD,
    "unexpected balance increase"
);
  1. Don’t dismiss audit findings because the behavior is “intentional.” Intent doesn’t equal safety.

  2. Monitor position concentration as a first-class risk metric, not an afterthought.

  3. Implement exchange rate change limits — if the rate jumps >X% in a single block, pause the market.

If You’re Auditing

  1. Test the full deposit surface, not just the intended entry points. Direct transfers, transferFrom with pre-approved amounts, and callback-based deposits should all be validated.

  2. Challenge “won’t fix” responses with concrete attack scenarios. “This is intended behavior” is not a security argument.

  3. Check for repeat vulnerabilities across different deployments of the same protocol. If it happened on zkSync, it can happen on BNB Chain.

If You’re Running a Lending Protocol

  1. Position concentration is a leading indicator. A single address at 84% of your supply cap is a red flag, not a feature.

  2. Historical exploits on your own protocol are the highest-signal intelligence you have. Venus ignored its own zkSync incident. Don’t repeat this pattern.

  3. Liquidation markets have limits. 254 competing bots still couldn’t prevent $2.15M in bad debt. Design for liquidation failure, not just liquidation success.

Timeline

  • June 2025 — Attacker begins accumulating THE via Tornado Cash funding
  • Feb 2025 — Venus zkSync deployment suffers similar donation attack ($700K+ bad debt)
  • Pre-attack — Community flags suspicious address; Venus declines action
  • Mar 15, 2026 11:00 UTC — Attacker deploys malicious contract, begins donation attack
  • Mar 15, 2026 ~12:00 UTC — 8,048 liquidation transactions begin unwinding the position
  • Post-attack — Venus suspends THE lending/borrowing, sets collateral factor to zero

The Venus incident is a masterclass in how DeFi protocols fail: not because the vulnerability was unknown, but because the warning was ignored. The audit found it. The community flagged it. A previous exploit demonstrated it. And still, three lines of defense crumbled against an attacker who took nine months to prepare.

The lesson isn’t “get better audits.” It’s listen to the audits you already have.

References: Venus Post-Mortem, BlockSec Analysis, Halborn Writeup

From Queries to Bytecode: The Final Pieces of SQLite’s Frontend

PS5 DualSense controller with haptic feedback — you already know what intentional haptic feedback feels like from your hands every day

Hello, I’m Maneshwar. I’m building git-lrc, an AI code reviewer that runs on every commit. It is free, unlimited, and source-available on Github. Star Us to help devs discover the project. Do give it a try and share your feedback for improving the product.

In the previous part, you saw how SQLite selects indexes and balances filtering with sorting.

Now we move into another important category of queries.

Aggregation and subqueries.

These introduce new challenges because SQLite is no longer just filtering rows.

It is grouping, transforming, and sometimes restructuring queries entirely.

How SQLite Executes GROUP BY

When you use a GROUP BY clause, SQLite introduces a special internal structure called an aggregator.

This is essentially a temporary table that stores:

  • A key → formed by GROUP BY columns
  • A value → aggregate data like COUNT, SUM, etc.

For example:

SELECT department, COUNT(*) FROM employees GROUP BY department;

SQLite processes this in two phases.

First, it scans rows and builds groups.

Then, it produces the final results.

The execution pattern looks like this:

where-begin  
    compute group-by key  
    focus on the group-by key  
    update aggregate terms  
where-end  
foreach group-by  
    compute result-set  
    send result to caller  
end-foreach

During the first phase, SQLite keeps updating aggregate values for each group.

During the second phase, it outputs the final computed results for each group

This approach ensures that rows are grouped efficiently without repeatedly scanning the same data.

The Problem with Subqueries in FROM

Now consider queries that use subqueries inside the FROM clause.

Example:

SELECT a FROM (
    SELECT x + y AS a FROM t1 WHERE z < 100
) WHERE a > 5;

The default way to execute this is:

  1. Run the inner query
  2. Store results in a temporary table
  3. Run the outer query on that table
  4. Delete the temporary table

This approach has a major drawback.

The temporary table has no indexes, so any filtering or joining done by the outer query becomes inefficient.

It also requires scanning data multiple times.

Subquery Flattening: A Smarter Approach

To avoid this overhead, SQLite uses an optimization called subquery flattening.

Instead of executing the subquery separately, SQLite merges it into the outer query.

The previous example becomes:

SELECT x + y AS a FROM t1 WHERE z < 100 AND a > 5;

Now the query can be executed in a single pass over the table.

This has two major benefits:

  • Eliminates temporary tables
  • Allows indexes on the base table to be used

This significantly improves performance

When Flattening Is Allowed

Flattening is not always possible.

SQLite applies this optimization only when a strict set of conditions is satisfied.

  1. The subquery and the outer query do not both use aggregates.
  2. The subquery is not an aggregate or the outer query is not a join.
  3. The subquery is not the right operand of a left outer join.
  4. The subquery is not DISTINCT or the outer query is not a join.
  5. The subquery is not DISTINCT or the outer query does not use aggregates.
  6. The subquery does not use aggregates or the outer query is not DISTINCT.
  7. The subquery has a FROM clause.
  8. The subquery does not use LIMIT or the outer query is not a join.
  9. The subquery does not use LIMIT or the outer query does not use aggregates.
  10. The subquery does not use aggregates or the outer query does not use LIMIT.
  11. The subquery and the outer query do not both have ORDER BY clauses.
  12. The subquery and outer query do not both use LIMIT.
  13. The subquery does not use OFFSET.
  14. The outer query is not part of a compound select or the subquery does not have both an ORDER BY and a LIMIT clause
  15. The outer query is not an aggregate or the subquery does not contain ORDER BY
  16. The sub-query is not a compound select, or it is a UNION ALL compound clause made up entirely of non-aggregate queries, and the parent query:
    • is not itself part of a compound select,
    • is not an aggregate or DISTINCT query, and
    • has no other tables or sub-selects in the FROM clause. The parent and sub-query may contain WHERE clauses.
    Subject to rules (11), (12) and (13), they may also contain ORDER BY, LIMIT and OFFSET clauses.
  17. If the sub-query is a compound select, then all terms of the ORDER by clause of the parent must be simple references to columns of the sub-query.
  18. The subquery does not use LIMIT or the outer query does not have a WHERE clause
  19. If the sub-query is a compound select, then it must not use an ORDER BY clause.

Query flattening is an important optimization when views are used because each use of a view is translated into a su bquery.

Fast MIN and MAX Queries

Aggregation is not always expensive.

SQLite has a very efficient optimization for queries like:

SELECT MIN(age) FROM users;

or

SELECT MAX(age) FROM users;

If there is no index on the column, SQLite must scan the entire table.

But if an index exists, SQLite can do something much faster.

It directly navigates to:

  • The first entry in the index for MIN
  • The last entry in the index for MAX

Since indexes are stored as B-trees, this operation takes logarithmic time, not linear time.

If the column is an INTEGER PRIMARY KEY, SQLite can even use the table’s primary B+ tree directly.

This makes MIN and MAX queries extremely efficient when proper indexing is in place

Bringing It All Together

At this point, you have seen how SQLite handles:

  • Filtering using WHERE
  • Choosing indexes
  • Ordering joins
  • Grouping results
  • Flattening subqueries
  • Optimizing aggregations

All of this happens before execution, inside the frontend.

By the time the Virtual Machine runs, everything has already been carefully planned and optimized.

Final Thoughts on the Frontend

The SQLite frontend is a complete pipeline:

  • The tokenizer breaks SQL into tokens
  • The parser builds structured representations
  • The optimizer reshapes queries for efficiency
  • The code generator produces executable bytecode

All of this work is triggered by a single function call:

sqlite3_prepare()

Query optimization remains one of the most complex and delicate parts of any database system.

SQLite keeps things relatively simple by using heuristics instead of heavy statistical models, but still manages to achieve strong performance in most real-world scenarios

What’s Next

We have now covered the entire frontend pipeline of SQLite, from raw SQL to optimized bytecode.

In the next series, we will move beyond compilation and explore how SQLite interacts with the outside world.

We will start with the SQLite Interface Handler, where queries enter the system and results are returned.

git-lrc
*AI agents write code fast. They also silently remove logic, change behavior, and introduce bugs — without telling you. You often find out in production.

git-lrc fixes this. It hooks into git commit and reviews every diff before it lands. 60-second setup. Completely free.*

Any feedback or contributors are welcome! It’s online, source-available, and ready for anyone to use.

⭐ Star it on GitHub:

GitHub logo

HexmosTech
/
git-lrc

Free, Unlimited AI Code Reviews That Run on Commit

AI agents write code fast. They also silently remove logic, change behavior, and introduce bugs — without telling you. You often find out in production.

git-lrc fixes this. It hooks into git commit and reviews every diff before it lands. 60-second setup. Completely free.

See It In Action

See git-lrc catch serious security issues such as leaked credentials, expensive cloud
operations, and sensitive material in log statements

git-lrc-intro-60s.mp4

Why

  • 🤖 AI agents silently break things. Code removed. Logic changed. Edge cases gone. You won’t notice until production.
  • 🔍 Catch it before it ships. AI-powered inline comments show you exactly what changed and what looks wrong.
  • 🔁 Build a…

Meet Good Design

In plain terms: software that’s easy to change without breaking what’s already there.

I hope you are starting to see the enemy. Complexity makes our systems hard to understand and hard to change — and left unchecked, it only compounds. But it helps to know not just what we’re fighting against, but what we’re fighting for. What does a system look like when complexity isn’t winning? What does it feel like to work in one?

Good design has a few common characteristics. They’re worth knowing, because they’re not just abstract ideals — they’re the goals that every tool and principle we’ll cover in this series is working toward.

The first thing you notice in a well-designed system is that it doesn’t ask too much of you. The information you need is clear and present. What a component does is apparent. You can reason about a part of the system without needing to hold the entire thing in your head — remember Ousterhout’s definition: anything that makes a system hard to understand and modify. A system that overwhelms you with things to know is a complex system, almost by definition.

Closely related is consistency. It operates at two levels. Within a component, consistency means the thing behaves predictably according to its own rules — you can learn how it works once and trust that it holds. Across the system, consistency means shared standards — naming conventions, patterns, error handling approaches — that make the codebase feel like it was written by one coherent mind rather than assembled from unrelated pieces. Independence gives different parts of a system the freedom to make their own decisions, but that freedom shouldn’t extend to arbitrariness. When different parts behave differently for no good reason, every part becomes its own puzzle. Inconsistency is a form of accidental complexity — it makes things harder to understand without making them any more capable. A consistent system respects the developer’s time and reduces the number of surprises.

The next cluster of qualities all relate to change — which makes sense, because software that can’t change gracefully is software that will become a burden. A well-designed system is flexible: you can change one part without disrupting the whole. The parts are independent enough to be worked on separately, to grow at their own pace, to be improved without requiring a coordinated effort across the entire codebase.

This flexibility is what makes a system stable. Stability here doesn’t mean frozen — it means that existing functionality is protected. When you add something new, you shouldn’t have to worry about what you might be breaking. When you fix a bug, the fix should stay contained. The risk of every change should be proportional to its scope, not amplified by hidden dependencies and tangled responsibilities.

Good design is also extendable. When a new requirement comes in, you shouldn’t have to rewrite what already exists — you should be able to build on it. This is one of the most practical tests of a design: can I add to this without dismantling it? Extendability is closely tied to reusability. Components that are focused and independent can be used in multiple places and contexts without being tailored to any specific one. You write something once, and it earns its keep many times over. Every time you reuse a well-designed component, you’re getting more value out of the original investment — and you’re not introducing new places for things to go wrong.

And then there’s testability. It might seem like a separate concern — a QA thing, not a design thing — but it’s actually one of the best signals of good design you have. It turns out that code that is clear, consistent, focused, and independent is also easy to test. You can test a component in isolation, without needing to set up the entire system around it. You can verify its behavior precisely and thoroughly. And when you have that kind of confidence in the individual parts, you have confidence in the whole. Testability isn’t just a nice-to-have — it’s a consequence of doing everything else right.

All of these qualities point toward the same thing. When your design is working, making a change means going to one place. Not one function or one line of code — that’s too literal. But one isolated, independent part of the system where the concern lives, where the change can be made, where it can be understood and tested without pulling the rest of the system into view. The change — whether it’s a new feature, a bug fix, a performance improvement, or a third-party integration — stays contained. The rest of the system doesn’t even notice.

That’s the goal. It’s optimistic, and real systems are messier than any ideal. But it’s a useful test: when you sit down to make a change, how many places do you have to touch? How much do you have to understand before you feel safe? The answer tells you a lot about the health of your design.

Now that we know what we’re fighting for, we can start talking about how to get there. The tools and principles we’ll cover from here aren’t abstract exercises — they’re the specific things that make these qualities possible. Each one is a way of pushing your design closer to the system you just read about.

I Built an Open-Source Alternative to $500/Year Trading Signal Tools

I was spending $47/month on TradingView Pro + 3Commas to get decent trading signals for XAUUSD, BTCUSD, and EURUSD. After a year, I cancelled both and built my own.

TradeClaw is now open source: https://github.com/naimkatiman/tradeclaw

What it does

  • Real-time buy/sell signals for 12 assets (XAUUSD, BTCUSD, ETHUSD, EURUSD, GBPUSD, and more)
  • Signal engine: weighted RSI (28%), MACD (20%), EMA crossovers (20%), Support/Resistance (20%), Bollinger Bands (6%), Stochastic (6%)
  • Backtesting against historical data
  • Telegram alerts when signals fire
  • Beautiful dark dashboard with candlestick charts

The cost comparison

Tool Monthly Annual
TradingView Pro $15 $180
3Commas Starter $29 $348
TradeClaw (self-hosted) $5 VPS $60

Deploy in 60 seconds

git clone https://github.com/naimkatiman/tradeclaw
cd tradeclaw
cp .env.example .env
docker compose up -d

Open http://localhost:3000. That’s it.

Why open source?

These tools shouldn’t cost $500/year. The technical analysis is well-understood math. The value isn’t in the algorithm — it’s in the UX, the integrations, and reliability.

TradeClaw is MIT licensed. Self-host it, modify it, contribute back.

Live demo: https://tradeclaw.win/demo
GitHub: https://github.com/naimkatiman/tradeclaw

⭐ Star it if you find it useful — it helps others discover the project.

LeetCode Solution: 1752. Check if Array Is Sorted and Rotated

Mastering Rotated Arrays: A Beginner-Friendly Look at LeetCode 1752

Hey fellow coders! 👋 Today, we’re diving into a super common and insightful LeetCode problem that’s perfect for strengthening your array manipulation skills: 1752. Check if Array Is Sorted and Rotated.

It sounds a bit tricky, but with a simple trick, we can unmask the mystery of these rotated arrays! Let’s conquer it together.

🚀 Problem Explanation

Imagine you have a perfectly sorted array of numbers in non-decreasing order (meaning numbers either go up or stay the same). For example: [1, 2, 3, 4, 5].

Now, imagine we take this array and “rotate” it. This means we move some elements from the beginning to the end, maintaining their relative order.

For instance, if we rotate [1, 2, 3, 4, 5] by 2 positions, 1 and 2 move to the end:
[3, 4, 5, 1, 2]

The problem asks us to determine: Given an array nums, can it be formed by taking some originally sorted, non-decreasing array and rotating it?

Important notes:

  • Non-decreasing: [1, 1, 2, 3] is sorted non-decreasingly. Duplicates are allowed!
  • Rotation: You can rotate by any number of positions, including zero (which means no rotation at all, just a plain sorted array).

Let’s look at the examples:

Example 1: nums = [3,4,5,1,2]

  • This can be [1,2,3,4,5] rotated by 2 positions.
  • Output: true

Example 2: nums = [2,1,3,4]

  • Can you find an originally sorted array that could become this after rotation? No.
  • Output: false

Example 3: nums = [1,2,3]

  • This is already sorted. It’s like rotating by 0 positions.
  • Output: true

Constraints are small (nums.length up to 100), so efficiency won’t be a huge bottleneck, allowing us to focus on the logic!

🤔 Intuition: The “One Break” Rule

This is where the “aha!” moment happens. Think about what happens when you rotate a sorted array.

  1. A perfectly sorted array (no rotation):
    [1, 2, 3, 4, 5]
    If you compare adjacent elements, nums[i-1] will always be less than or equal to nums[i]. There are no “breaks” where the order goes down.

  2. A sorted array rotated once:
    [3, 4, 5, 1, 2]
    Notice how most elements maintain their non-decreasing order: 3 <= 4, 4 <= 5, 1 <= 2.
    But there’s exactly one spot where this breaks: 5 > 1. This is the point where the array “wraps around” from its largest element back to its smallest.

  3. An array that’s NOT sorted and rotated:
    [2, 1, 3, 4]
    Here, 2 > 1 is a break. And then 3 <= 4 is fine. What if we consider the “wrap-around” too? If it was [1,2,3,4] rotated, it would have one break. But [2,1,3,4] has 2>1. If we trace the original array [1,2,3,4] rotated one time, it would be [4,1,2,3], which also has only one break (4>1).
    The key insight is: A valid sorted and rotated array (including 0 rotations) will have at most one point where nums[i-1] > nums[i]. This “break” signifies the point of rotation. If we find more than one such break, it means the array wasn’t originally sorted.

Let’s test this “one break” rule:

  • [1,2,3,4,5] – 0 breaks. (0 <= 1 is true) ✅
  • [3,4,5,1,2] – 1 break (5 > 1). (1 <= 1 is true) ✅
  • [2,1,3,4] – 2 breaks (2 > 1 and 4 > 2 if we consider circularity). (2 <= 1 is false) ❌

This single “break count” idea will be our guiding light!

🪜 Approach: Counting the Descents

Based on our intuition, here’s the step-by-step approach:

  1. Initialize a counter: Let’s call it count, and set it to 0. This count will keep track of how many times we find nums[i-1] > nums[i].
  2. Iterate through the array (non-circularly): Loop from the second element (i=1) up to the last element (i=n-1).
    • In each step, compare nums[i-1] with nums[i].
    • If nums[i-1] > nums[i], it means we’ve found a “descent” or a “break” in the non-decreasing order. Increment count.
  3. Check the “wrap-around” break: After the loop, we need to account for the possibility that the rotation break happens between the last element and the first element.
    • Compare nums[n-1] (the last element) with nums[0] (the first element).
    • If nums[n-1] > nums[0], it means the “wrap-around” also caused a break. Increment count.
  4. Final Decision:
    • If count is 0 or 1, return true. This means the array was originally sorted and rotated (0 rotations for count=0, 1 rotation for count=1).
    • If count is greater than 1, return false. This indicates more than one break, meaning the array wasn’t a simple sorted-and-rotated sequence.

Let’s walk through [2,1,3,4] again with this precise logic:

  • count = 0
  • n = 4
  • Loop:
    • i=1: nums[0]=2, nums[1]=1. 2 > 1 is true. count becomes 1.
    • i=2: nums[1]=1, nums[2]=3. 1 > 3 is false. count remains 1.
    • i=3: nums[2]=3, nums[3]=4. 3 > 4 is false. count remains 1.
  • After loop: count = 1.
  • Wrap-around check: nums[n-1]=4, nums[0]=2. 4 > 2 is true. count becomes 2.
  • Final Decision: count is 2. Since 2 > 1, we return false. Correct!

This approach correctly identifies all the “breaks” in the circular order and gives us the answer!

💻 Code

Here’s the C++ implementation using the approach we just discussed:

class Solution {
public:
    bool check(std::vector<int>& nums) {
        int count = 0; // Initialize a counter for "descents" or "breaks"
        int n = nums.size(); // Get the size of the array

        // Step 1: Iterate through the array to find internal breaks
        // We compare nums[i-1] with nums[i]
        // Example: in [3,4,5,1,2], the break 5 > 1 is found here.
        for (int i = 1; i < n; i++) {
            if (nums[i-1] > nums[i]) {
                count++; // Increment count if a descent is found
            }
        }

        // Step 2: Check the "wrap-around" break
        // This handles cases like [1,2,3] where there's no internal break,
        // but if we consider it circularly (3 -> 1), it's a "descent".
        // Also handles the actual rotation point for arrays like [4,5,1,2,3] where 5 > 1 is the break.
        // It complements the loop by checking the (last, first) pair.
        if (nums[n-1] > nums[0]) {
            count++; // Increment count if the wrap-around is a descent
        }

        // Step 3: Determine if the array is sorted and rotated
        // A truly sorted and rotated array (including 0 rotations)
        // will have at most one "break" in the non-decreasing sequence.
        // If count is 0, it's perfectly sorted.
        // If count is 1, it's sorted and rotated once.
        // If count > 1, it's not a sorted and rotated array.
        return count <= 1;
    }
};

⏱️ Time & Space Complexity Analysis

  • Time Complexity: O(N)

    • We iterate through the nums array exactly once in the for loop, which takes O(N) time where N is the number of elements in nums.
    • The additional if condition and variable assignments are constant time operations.
    • Therefore, the total time complexity is linear with respect to the input array size.
  • Space Complexity: O(1)

    • We only use a few extra variables (count, n) to store our state. These variables take up a constant amount of memory, regardless of the input array’s size.
    • Thus, the space complexity is constant.

✨ Key Takeaways

  • The “One Break” Rule: The most crucial insight for this problem is realizing that a sorted array, when rotated, will have at most one point where the non-decreasing order is broken. This single “descent” marks the transition from the largest elements back to the smallest ones.
  • Circular Thinking: Many array problems, especially those involving rotations, benefit from thinking about the array as a circular structure. Even if you don’t explicitly use the modulo operator (i+1)%n, remembering to check the (last, first) pair is vital.
  • Simple Iteration: A single pass through the array is often enough to gather all the necessary information for many array-based problems.

This problem is a fantastic way to practice identifying patterns in arrays and translating those patterns into clean, efficient code. Keep practicing, and happy coding!

Author: Vansh2710
Published: 2026-03-28 23:13:11

Claude Code Just Hit #1 on Hacker News. Here’s Everything You Need to Know.

Claude Code hit #1 on Hacker News today. The post — a deep dive into the .claude/ folder anatomy — pulled 556 points and counting. Five YouTube tutorials dropped in the last 48 hours. X is buzzing with auto-fix demos, hooks configurations, and cloud session workflows. And Anthropic just shipped conditional hooks and cloud-based auto-fix in the same week.

Something is happening. Claude Code isn’t just a developer tool anymore — it’s becoming the default way a new generation of builders creates software. Prosumers who’ve never opened a terminal are cloning repos and shipping sites. Senior engineers are restructuring their entire CI/CD pipelines around it. The adoption curve isn’t linear — it’s vertical.

This guide covers everything. Installation to advanced workflows. Whether you’re opening Claude Code for the first time or you’re ready to wire it into your CI pipeline with hooks and auto-fix, this is the resource you bookmark and come back to.

📊 The adoption signal is loud: Chase AI posted 5 Claude Code tutorials in two days. Kenny Liao dropped a beginner-to-mastery deep dive. Matthew Berman ranked Claude S-tier in his March 2026 model tier list — “unbelievable, good at everything.” This isn’t just developer content anymore. It’s prosumer builders using Claude Code as their default “build anything fast” tool.

What Is Claude Code?

Claude Code is Anthropic’s command-line AI coding agent. Unlike chat-based AI assistants that suggest code snippets, Claude Code operates directly in your terminal — reading your files, understanding your project structure, writing code, running tests, committing to git, and executing shell commands. It’s an autonomous agent, not an autocomplete engine.

Think of the difference like this: GitHub Copilot is a passenger giving directions. Claude Code is a driver who knows the roads, checks the mirrors, and parallel parks.

It runs on Anthropic’s Claude models (currently Opus 4.6 by default for Max subscribers, Sonnet 4.5 for Pro) with a massive context window — up to 1 million tokens. That means it can hold your entire codebase in its head while working.

Installation and Setup

Prerequisites

You need:

  • Node.js 18+ (Claude Code is an npm package)
  • An Anthropic account with a Max subscription ($100/month for unlimited Opus 4.6) or Pro ($20/month with Sonnet 4.5 and limited Opus)
  • A terminal — macOS Terminal, iTerm2, Windows Terminal, or any Linux terminal
  • Git installed and configured

Install Claude Code

npm install -g @anthropic-ai/claude-code

That’s it. One command. No Docker containers, no Python virtual environments, no config files to create first.

Authenticate

claude

Running claude for the first time opens a browser window for OAuth authentication with your Anthropic account. Once authenticated, the token is stored locally and you’re ready to go.

Verify Your Installation

claude --version
claude /doctor

The /doctor command checks your environment — Node version, authentication status, git configuration, and available tools.

⚡ Pro tip: If you’re on macOS, install via Homebrew for automatic updates: brew install claude-code. The npm install works everywhere, but Homebrew keeps you on the latest version without thinking about it.

Your First Project Walkthrough

Let’s build something real. Open a terminal, navigate to a project directory (or create a new one), and start Claude Code:

mkdir my-first-project && cd my-first-project
git init
claude

Claude Code launches in interactive mode. You’ll see a prompt where you can type natural language instructions. Try this:

Create a React app with TypeScript that displays a real-time 
cryptocurrency price dashboard. Use Vite for the build tool, 
Tailwind CSS for styling, and the CoinGecko free API for data. 
Include a search bar, favorites list, and auto-refresh every 30 seconds.

Watch what happens. Claude Code will:

  1. Plan — outline the architecture and file structure
  2. Scaffold — create package.json, vite.config.ts, tsconfig.json, Tailwind config
  3. Implement — write components, hooks, API integration, types
  4. Configure — set up routing, environment variables, dev scripts
  5. Test — run the dev server to verify everything works

The entire process takes 3-5 minutes.

The .claude/ Folder: Your Project’s Brain

This is what hit #1 on Hacker News. The .claude/ folder is where Claude Code stores its understanding of your project. Think of it as the configuration layer between “generic AI” and “AI that knows your codebase.”

Here’s the anatomy:

.claude/
├── CLAUDE.md           # Project instructions (the big one)
├── settings.json       # Claude Code configuration
├── settings.local.json # Local overrides (gitignored)
├── commands/           # Custom slash commands
│   ├── review.md       # /review command
│   └── deploy.md       # /deploy command
├── skills/             # Reusable capabilities
│   └── my-skill/
│       └── skill.md
└── rules/              # Constraints and patterns
    ├── no-any.md
    └── error-handling.md

CLAUDE.md — The Most Important File

CLAUDE.md is the instruction manual for Claude Code in your project. When Claude starts a session, it reads this file first. Everything in it shapes how Claude understands and works with your code.

A good CLAUDE.md includes:

# Project: CryptoDash

## Architecture
- React 18 + TypeScript + Vite
- State management: Zustand (NOT Redux — we migrated away in v2.1)
- API layer: TanStack Query with custom hooks in src/hooks/api/
- Styling: Tailwind CSS with custom design tokens in tailwind.config.ts

## Conventions
- All components use named exports (not default exports)
- API hooks follow the pattern: useGet{Resource}, useMutate{Resource}
- Error boundaries wrap every route-level component
- Tests colocate with source: Component.tsx → Component.test.tsx

## Do NOT
- Use any type — use unknown with type guards instead
- Import from barrel files (index.ts) in the same package
- Add dependencies without checking bundle size impact first

The CLAUDE.md Hierarchy

Claude Code reads multiple CLAUDE.md files in priority order:

  1. ~/.claude/CLAUDE.md — Global instructions (your personal coding style, always applied)
  2. ./CLAUDE.md or ./.claude/CLAUDE.md — Project root (team-shared, committed to git)
  3. ./src/CLAUDE.md — Directory-specific (instructions for specific parts of the codebase)

Deeper files override shallower ones. Think of CLAUDE.md files like .gitignore — they cascade from general to specific.

The Hooks System: Automating Everything

Hooks are where Claude Code transforms from “AI assistant” to “development platform.” They’re user-defined commands that execute automatically at specific points in Claude Code’s lifecycle.

Hook Events

Event When It Fires Common Use
SessionStart Session begins/resumes Load environment, inject context
PreToolUse Before a tool call Block dangerous commands, validate
PostToolUse After a tool call succeeds Auto-format, lint, notify
Notification Claude needs attention Desktop alerts, Slack messages
SubagentStart Subagent spawned Log, resource management
PreCompact Before context compaction Save important context
SessionEnd Session ends Cleanup, report generation

Your First Hook: Auto-Format on Save

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write",
        "hooks": [
          {
            "type": "command",
            "command": "npx prettier --write "$CLAUDE_FILE_PATH""
          }
        ]
      }
    ]
  }
}

Every time Claude writes a file, Prettier formats it automatically.

Conditional Hooks with if

This just shipped this week — the if field enables conditional hook execution:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "echo 'Blocked: no direct DB access in production'",
            "if": "echo $CLAUDE_TOOL_INPUT | grep -q 'psql.*prod'"
          }
        ]
      }
    ]
  }
}

Prompt-Based Hooks: AI Reviewing AI

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write",
        "hooks": [
          {
            "type": "prompt",
            "prompt": "Review this file change for security vulnerabilities. If you find any, return BLOCK. If safe, return ALLOW.",
            "model": "claude-haiku-4"
          }
        ]
      }
    ]
  }
}

A cheaper, faster model (Haiku) reviews every file write for security issues. AI auditing AI.

Auto-Fix: Claude Code Meets CI/CD

Auto-fix lets Claude Code automatically monitor your pull requests and fix CI failures — linting errors, type errors, failing tests — without you lifting a finger.

The magic: this now runs in the cloud. Your laptop can be closed. Claude Code cloud sessions monitor your PRs and fix them autonomously.

Setting Up Auto-Fix

{
  "autoFix": {
    "enabled": true,
    "github": {
      "ciChecks": ["test", "lint", "typecheck", "build"],
      "maxAttempts": 3,
      "branchPattern": "feat/*"
    }
  }
}

The CI Integration Pattern

name: Claude Auto-Fix
on:
  check_suite:
    types: [completed]

jobs:
  autofix:
    if: github.event.check_suite.conclusion == 'failure'
    runs-on: ubuntu-latest
    steps:
      - uses: anthropic/claude-code-action@v1
        with:
          mode: auto-fix
          max-attempts: 3

⚡ Real-world usage pattern: Start with auto-fix on lint and type errors only. Graduate to test failures once you trust the workflow. Never auto-fix security scans automatically.

Cloud Sessions and Remote Tasks

Anthropic shipped Remote Tasks — the ability to run Claude Code sessions on Anthropic’s cloud infrastructure, triggered on schedules or events.

  • Scheduled maintenance — “Every Monday at 9 AM, audit dependencies and open PRs for outdated packages”
  • Event-driven workflows — “When a new issue is labeled bug, create a branch, investigate, and open a draft PR”
  • Continuous documentation — “After every merge to main, update the API docs and changelog”

Advanced Workflows

Multi-Agent Architecture with Subagents

Claude Code can spawn subagents — isolated Claude instances that handle specific subtasks:

Main Agent (Opus 4.6)
├── Subagent 1: "Implement the API endpoints" (Opus)
├── Subagent 2: "Write tests for the API" (Sonnet)
├── Subagent 3: "Update documentation" (Haiku)
└── Subagent 4: "Review all changes" (Opus)

The Harness Engineering Pattern

The most sophisticated Claude Code users build harnesses around it — CLAUDE.md files, hooks, custom commands, MCP integrations, review pipelines, and CI workflows.

The same model scored 78% with one harness and 42% with another. The harness matters more than the model.

Pricing

Plan Price Model Access
Pro $20/month Sonnet 4.5 (default), limited Opus
Max 5x $100/month Opus 4.6 (default), unlimited Sonnet
Max 20x $200/month Opus 4.6 extended, priority access
API Pay-per-token Any model

Tips That Actually Matter

  1. Write Your CLAUDE.md Before Writing Code — 15 minutes saves hours.
  2. Use /compact Strategically — Run it when Claude starts making mistakes.
  3. Start Conversations with Context — Reference specific files and patterns.
  4. Git Commit Frequently — Gives you rollback points.
  5. Trust But Verify — Always review the diff before merging.
  6. Use the Right Model for the Job — Opus for architecture, Sonnet for features, Haiku for docs.
  7. Hooks Are Your Guardrails — PreToolUse to block, PostToolUse to format, Notification to alert.

Start Building

npm install -g @anthropic-ai/claude-code

Create a CLAUDE.md. Set up your first hook. Push a PR and let auto-fix handle the lint errors. Build something that would have taken you a week — in an afternoon.

Originally published on ComputeLeap. For more on the Claude Code ecosystem, check out our coverage of Remote Tasks and Cloud Sessions and Harness Engineering.

Your Security Scanner Was the Weapon: Inside the Trivy Supply Chain Attack

TL;DR: Trivy, the most widely used container scanning action in GitHub Actions, was compromised on March 19, 2026. A threat actor poisoned 76 of its 77 version tags. Every pipeline that ran a scan silently handed over SSH keys, cloud credentials, Kubernetes tokens, and more. The scan appeared to succeed. You’d never know.

The Setup

I’ve had Trivy in my pipelines for years. Container scanning on every PR, every merge, every deploy. It’s one of those things you set up once and stop thinking about, which is exactly what makes this attack so effective.

On March 19, 2026, a threat actor group called TeamPCP force-pushed malicious commits to 76 of the 77 version tags in the aquasecurity/trivy-action GitHub repository. All 7 tags in aquasecurity/setup-trivy were also compromised. If your workflow referenced Trivy by a tag (which is how basically everyone references GitHub Actions), you were running their code.

The scanner still ran. Your pipeline still went green. You had no idea.

How It Actually Happened

This attack didn’t start on March 19. It started weeks earlier.

Late February 2026: An automated bot called “hackerbot-claw” exploited a misconfigured GitHub Actions workflow and stole a privileged Personal Access Token from Aqua Security’s CI environment. The attacker used this to push malware to the Trivy VS Code extension on Open VSX.

March 1: Aqua Security disclosed the incident publicly via a GitHub discussion and rotated credentials. Except the rotation was incomplete. One service account, one PAT, one residual access path, still live.

March 19, 17:43 UTC: Using the still-valid credentials, TeamPCP force-pushed malicious commits to 76 of 77 tags in trivy-action and all 7 tags in setup-trivy. The compromised commits spoofed legitimate maintainer identities. GitHub itself flagged them with “This commit does not belong to any branch on this repository” but that warning is easy to miss in a workflow log.

March 19, 18:22 UTC: A rogue commit published a malicious Trivy binary as v0.69.4 across every distribution channel simultaneously: GitHub Releases, GHCR, Docker Hub, ECR Public, deb/rpm repositories, and get.trivy.dev.

March 20, 05:40 UTC: Aqua remediated the trivy-action tags. The window was roughly 12 hours.

March 22: The attacker pushed additional malicious Docker Hub images (v0.69.5, v0.69.6, latest) using separately compromised Docker Hub credentials, bypassing all GitHub controls. Same day, 44 repositories in Aqua’s aquasec-com GitHub org were defaced using a stolen service account token that bridged both orgs.

March 24: The campaign expanded to Checkmarx KICS and LiteLLM PyPI packages (1.82.7, 1.82.8).

The takeaway here is not just that a tool got compromised. It’s that incomplete remediation turned a single breach into a three-week campaign.

What the Payload Did

This is the part that should make you uncomfortable.

The malicious entrypoint.sh prepended about 105 lines of attack code before the legitimate Trivy scanner logic. The scan completed normally. Your logs looked fine.

Stage 1: Process enumeration. The script scanned /proc/*/environ across all runner processes, extracting environment-level secrets, filtering for anything with env or ssh in the name.

Stage 2: Memory scraping. Here’s where it gets clever. On GitHub-hosted runners, a base64-encoded Python script located the Runner.Worker process, read its memory maps via /proc/{PID}/maps, and scraped raw process memory via /proc/{PID}/mem. It was targeting GitHub Actions secrets specifically, looking for JSON structures matching "name":{"value":"...","isSecret":true}.

This matters because it bypassed GitHub Actions’ secret masking. Values that would show as *** in your logs were extracted in plaintext from process memory. There’s no log artifact. Nothing looked wrong.

On self-hosted runners, a different payload did broad filesystem harvesting across 50+ target paths:

  • SSH private keys and configs
  • Cloud credentials (AWS, GCP, Azure)
  • Kubernetes configs and service account tokens
  • Docker registry credentials
  • Database credentials (MySQL, PostgreSQL, MongoDB, Redis)
  • Terraform state files and variables
  • Ansible vaults and Helm values
  • .env files and API keys
  • Git credentials and shell history
  • Cryptocurrency wallet keys

Stage 3: Encryption. Collected data was encrypted with AES-256-CBC using a randomly generated session key, then the session key was wrapped with a hardcoded 4096-bit RSA public key. Only the attacker’s server could decrypt it.

Stage 4: Exfiltration. Primary channel was an HTTPS POST to scan.aquasecurtiy[.]org, a typosquatted domain designed to look like Aqua Security’s legitimate domain (note the extra ‘i’ in ‘security’). Resolves to 45.148.10.212 in Amsterdam.

Fallback: if the C2 was unreachable and a GitHub PAT was in the environment, the malware created a public repository named tpcp-docs and uploaded stolen data as release assets, turning GitHub itself into the exfiltration channel.

Are You Affected?

Check these specific exposure windows:

Component Affected Versions Exposure Window Safe
trivy binary v0.69.4 ~3h (Mar 19) v0.69.3 or earlier
trivy Docker Hub v0.69.5, v0.69.6, latest ~10h (Mar 22–24) v0.69.3 or earlier
trivy-action Tags 0.0.1–0.34.2 ~12h (Mar 19–20) v0.35.0+ or SHA-pinned
setup-trivy All 7 tags ~12h (Mar 19–20) SHA-pinned
LiteLLM PyPI 1.82.7, 1.82.8 Mar 24+ 1.82.6 or earlier

If you ran Trivy in any pipeline during those windows and weren’t pinning to a commit SHA, you have to assume secrets were stolen. All of them. Every secret accessible from that runner environment.

What You Need to Change

This is the remediation checklist, ordered by priority.

1. Rotate first, investigate second

If you were in the exposure window, rotate everything the runner could have touched. Don’t wait for confirmation. Treat every secret as compromised:

  • AWS access keys and IAM roles
  • GCP service account keys
  • Azure service principals
  • Kubernetes service account tokens
  • Docker registry credentials
  • SSH keys
  • Database credentials
  • GitHub PATs and tokens

2. Pin actions to commit SHAs

This is the single most effective structural change. Tags are mutable. Commit SHAs are not.

# Bad — this is what everyone does, and what got compromised
- uses: aquasecurity/[email protected]

# Good — SHA-pinned, immutable
- uses: aquasecurity/trivy-action@57a97c7843d7da7a7b4f8ce2a0c4e3b7f0c2e1d  # 0.35.0

Yes, it’s more work to update. That’s the point. Renovatebot or Dependabot can automate SHA updates if you configure them for Actions.

3. Switch to OIDC for cloud authentication

Long-lived cloud credentials in CI are a liability. OIDC lets your runner authenticate to AWS, GCP, or Azure without storing static keys:

# AWS example
- uses: aws-actions/configure-aws-credentials@v4
  with:
    role-to-assume: arn:aws:iam::ACCOUNT:role/github-actions-role
    aws-region: us-east-1

Nothing to steal if there’s nothing stored. The credentials are ephemeral and scoped to the job.

4. Restrict runner permissions

GitHub Actions runners get GITHUB_TOKEN by default. Scope it down:

permissions:
  contents: read
  security-events: write
  # Nothing else

Most workflows need far less than the default. Less permission means smaller blast radius.

5. Audit non-human identities

The Trivy attack persisted because one service account credential wasn’t rotated. Audit all machine identities in your org:

  • GitHub PATs: Who issued them? When do they expire? Are they scoped minimally?
  • Service accounts: Which ones have write access to release infrastructure?
  • Bot accounts: Are any shared across orgs or repositories?

Long-lived, over-privileged service accounts are how a one-time breach becomes a three-week campaign.

6. Use secret scanning

GitGuardian, GitHub’s native secret scanning, or both. The Trivy attacker used GitHub as a fallback exfiltration channel. If your credentials ever end up in a public repo, you want to know in minutes, not days.

7. Verify binaries before running them

For direct binary downloads (not GitHub Actions), verify checksums:

# Download the official checksums
curl -sSL https://github.com/aquasecurity/trivy/releases/download/v0.69.3/trivy_0.69.3_checksums.txt -o checksums.txt

# Verify your binary
sha256sum -c checksums.txt --ignore-missing

If your pipeline downloads and runs binaries from the internet, add checksum verification as a step.

The Real Lesson

The Trivy attack was technically sophisticated, but the root cause is unglamorous: incomplete credential rotation.

Aqua disclosed the initial breach on March 1 and rotated credentials. One PAT, one service account, one residual access path was left active. That’s what TeamPCP used on March 19. The March 22 Docker Hub compromise used yet another separate credential that wasn’t in scope of the original remediation.

When you rotate secrets after a breach, you need to be exhaustive. Enumerate every credential that could have been exposed, every service account that had access, every integration that used a compromised token. Rotation is not a task you do until it feels complete. It’s a task you do until you’ve verified every access path is severed.

The other lesson: the attack surface for CI/CD is enormous. Your pipeline runs with access to secrets, cloud credentials, internal infrastructure. When you add a third-party action, you’re trusting that maintainer’s entire security posture, including their CI, their service accounts, and their credential management practices. SHA pinning doesn’t eliminate that trust but it gives you a stable, auditable point you can reason about.

Immediate Checklist

[ ] Check pipeline logs for trivy-action usage between March 19–20
[ ] Check pipeline logs for trivy binary v0.69.4 usage on March 19
[ ] Check for Docker image usage of v0.69.5, v0.69.6, or latest between Mar 22–24
[ ] Rotate all secrets accessible from affected runners
[ ] Update trivy-action to v0.35.0 or pin to SHA
[ ] Check for LiteLLM usage of 1.82.7 or 1.82.8
[ ] Switch cloud auth to OIDC
[ ] Pin all third-party actions to commit SHAs
[ ] Restrict workflow permissions to minimum required
[ ] Audit service accounts and PATs for expiry and scope
[ ] Enable secret scanning on your org

References:

The command-line interface (CLI) is a powerful tool that allows developers to interact with their operating system and execute commands to p

Introduction

The command-line interface (CLI) is a powerful tool that allows developers to interact with their operating system and execute commands to perform various tasks. As a beginner to intermediate developer, learning how to use the CLI is an essential skill that can help you become more efficient and effective in your work. In this tutorial, we will cover the basics of getting started with the CLI and provide you with hands-on experience through practical examples.

The CLI is a text-based interface that allows you to type commands to execute specific tasks. It provides a lot of flexibility and can be used to perform tasks such as file management, system configuration, and debugging. With the CLI, you can automate tasks, create scripts, and even build custom tools to simplify your workflow. Whether you are working on a Windows, macOS, or Linux system, the CLI is an essential tool that you should be familiar with.

In this tutorial, we will focus on the basics of the CLI and provide you with a comprehensive guide on how to get started. We will cover the prerequisites, basic commands, navigation, and file management. By the end of this tutorial, you will have a solid understanding of the CLI and be able to use it to perform various tasks with confidence.

Prerequisites

Before you start using the CLI, you need to have the following prerequisites:

  • A computer with a Windows, macOS, or Linux operating system
  • A terminal emulator or command prompt (e.g., Command Prompt on Windows, Terminal on macOS or Linux)
  • Basic knowledge of computer concepts and terminology

Main Content

Section 1: Basic Commands

To get started with the CLI, you need to know some basic commands. Here are a few essential commands to get you started:

  • cd: change directory
  • pwd: print working directory
  • ls: list files and directories
  • mkdir: make a new directory
  • rm: remove a file or directory

Let’s try some examples:

# Create a new directory
mkdir mydirectory

# Change into the new directory
cd mydirectory

# List the files and directories in the current directory
ls

# Print the current working directory
pwd

Section 2: Navigation

Navigation is a critical aspect of using the CLI. Here are some essential navigation commands:

  • cd ~: change to the home directory
  • cd ..: change to the parent directory
  • cd ./: change to the current directory
  • cd ../../: change to the grandparent directory

Let’s try some examples:

# Change to the home directory
cd ~

# Change to the parent directory
cd ..

# Change to the current directory
cd ./

# Change to the grandparent directory
cd ../../

Section 3: File Management

File management is another essential aspect of using the CLI. Here are some basic file management commands:

  • touch: create a new empty file
  • cp: copy a file
  • mv: move or rename a file
  • rm: remove a file

Let’s try some examples:

# Create a new empty file
touch myfile.txt

# Copy the file
cp myfile.txt mycopy.txt

# Move or rename the file
mv myfile.txt mynewfile.txt

# Remove the file
rm mynewfile.txt

Section 4: Advanced Commands

As you become more comfortable with the CLI, you can start using more advanced commands. Here are a few examples:

  • grep: search for a pattern in a file
  • sed: stream editor for filtering and transforming text
  • awk: pattern scanning and text processing language

Let’s try some examples:

# Search for a pattern in a file
grep "pattern" myfile.txt

# Use sed to replace a pattern in a file
sed "s/pattern/replacement/" myfile.txt

# Use awk to print the first column of a file
awk '{print $1}' myfile.txt

Troubleshooting

As you start using the CLI, you may encounter some errors or issues. Here are some common troubleshooting tips:

  • Check the command syntax: Make sure you are typing the command correctly, including any options or arguments.
  • Check the file permissions: Make sure you have the necessary permissions to read, write, or execute the file.
  • Check the directory: Make sure you are in the correct directory and that the file or directory exists.

Some common error messages include:

  • command not found: The command is not recognized or installed.
  • permission denied: You do not have the necessary permissions to perform the action.
  • file not found: The file or directory does not exist.

Conclusion

In this tutorial, we covered the basics of getting started with the CLI. We introduced you to basic commands, navigation, file management, and advanced commands. We also provided you with some troubleshooting tips to help you overcome common errors or issues. With practice and experience, you will become more comfortable using the CLI and be able to perform various tasks with confidence. Remember to always check the command syntax, file permissions, and directory before performing any action. Happy coding!

Sponsor & Subscribe

Want weekly practical tutorials and collaboration opportunities?

How I Built an AI-Powered Business Brain in One Day Using Notion MCP

What I Built

Video Demo

This is a submission for the Notion MCP Challenge

What I Built

The Jesse System AI Brain — a fully connected personal and business productivity system powered by Notion MCP and Claude AI.

The Jesse System is a small business automation consulting service that helps small business owners stop doing repetitive tasks manually. Today I built the entire infrastructure for it in a single session using Notion MCP as the backbone.

Here’s what the system includes:

  • A structured Notion workspace with five interconnected sections: AI Conversations, Personal To-Dos, Market Research, Game Project, and Small Business
  • Claude connected to Notion via MCP — reading, writing, updating, and logging entries in real time through natural language
  • A live business website deployed at dulcet-churros-460036.netlify.app with an interactive Zapier explainer, pricing, and contact form
  • A real-time AI conversation logger — so nothing important discussed with any AI tool ever gets lost again
  • A business model built and documented inside Notion — with real 2026 market pricing data
  • A game concept developed and stored — Nurses vs. Zombies, a sci-fi comedy FPS (“Doom meets Scrubs”)

The core idea: Notion becomes the brain that any AI can read from and write to — solving the real problem of losing context between AI sessions.

Video Demo

Show Us the Code

The live site is a single deployable HTML file hosted on Netlify:
dulcet-churros-460036.netlify.app

Claude Desktop was connected to Notion MCP using this config at %APPDATA%Claudeclaude_desktop_config.json:

{
  "mcpServers": {
    "notionMCP": {
      "command": "npx",
      "args": ["-y", "mcp-remote", "https://mcp.notion.com/mcp"]
    }
  }
}

The Claude.ai web connector was also used via https://mcp.notion.com/mcp with full OAuth authorization and read/write access.

How I Used Notion MCP

Notion MCP was the core of everything built today. Here’s exactly what it did:

Real-time read/write via natural language — Claude read and wrote to Notion pages throughout the entire session without me touching Notion manually. I’d say “log today’s session” and it appeared in my AI Conversations database instantly.

Living business documentation — The Jesse System’s business description, pricing model, market research, and game concept were all built and stored directly into Notion through Claude. The workspace is now a working knowledge base any AI can reference.

Context persistence across sessions — The biggest problem I was solving for myself: I kept forgetting what I’d told which AI. Notion MCP means Claude can now look at my workspace and immediately know my projects, my business, and my history — no re-explaining required.

To-do management — Tasks were added to my Personal To-Dos page mid-conversation without interrupting the flow of work.

The integration dropped near the deadline due to a session conflict, but was fully functional throughout development with 13 Notion tools active including search, fetch, create-page, update-page, and move-pages.

“Someone running a business isn’t looking for showmanship. They’re looking for simplicity and clarity.” — That’s the philosophy behind The Jesse System, and behind using Notion MCP as the foundation.